데이터 로깅 애플리케이션을 작성 중인데 모든 것이 다음과 같이 시작됩니다.
./program > out.bin
데이터 수집기는 정기적으로 stdout 출력 파일을 집계하고 데이터를 읽습니다.
문제는 IO 스트림이 버퍼링되어 일부 프로그램이 초당 1바이트로 데이터를 출력하는 경우 실제로 데이터를 플러시하는 데 오랜 시간(기본 4kB 버퍼 크기, 최대 4k초)이 걸린다는 것입니다.
내 질문은 stdout/pipe/printf 버퍼를 외부에서 강제로 플러시하는 방법입니다. 즉, fflush(stdout)
.
나는 다음과 같은 다양한 웹 사이트를 읽었습니다.파이프라인에서 버퍼링 끄기, 그러나 IO 성능(측정)에 큰 영향을 미치기 때문에 버퍼를 비활성화할 수 없습니다.
저는 고성능 생산 솔루션을 찾고 있으며 항상 다음 기준을 충족합니다.
- 프로그램(데이터 생산자) PID는 항상 알려져 있습니다.
- 출력은 항상 알려진 경로가 있는 파일입니다.
- 데이터 로깅 프로세스에는 전체 루트 액세스 권한이 있습니다.
답변1
gdb -p PID -batch -ex 'p fflush(stdout)'
모든 디버깅 및 해킹과 마찬가지로 YMMV.
답변2
실행 중인 프로그램의 소스 코드에 접근할 수 있나요?
이론적으로 불가능하지는 않지만 임의의 실행 파일을 강제로 새로 고치는 것은 매우 어렵습니다. fflush
코드에서 함수와 매개변수를 찾은 다음 stdout
프로그램 실행을 중단하고 호출을 예약한 fflush
다음 실행을 계속해야 합니다. 프로그램이 공유 라이브러리를 사용하는 경우 최소한 첫 번째 부분은 fflush
합계를 찾는 것이 더 쉬워지지만 stdout
여전히 호출을 모의해야 합니다. 또한 알 수 없는 바이너리의 경우 stdio를 사용하는지 또는 자체 버퍼링 메커니즘을 구현하는지 알 수 없습니다. 출력 파일의 경로를 아는 것은 도움이 되지 않습니다.
gdb
명령을 사용 하여 attach
프로세스에 연결해 볼 수 있습니다 . 어쩌면 gdb
함수가 fflush
호출될 수도 있습니다.
프로그램의 소스 코드가 있는 경우 버퍼를 플러시하고 버퍼를 플러시해야 할 때 신호를 보내는 신호 처리기를 구현하십시오.
파이프를 사용해 볼 수 있습니다. 출력이 파이프인 경우 프로그램이 버퍼링하지 못할 수 있습니다. 그것을로 바꾸다
./program | your_program > out.bin
프로그램은 입력을 받아 버퍼링하고 신호가 수신되면 버퍼를 플러시할 수 있습니다. 여전히 CPU 오버헤드가 추가되지만 디스크 오버헤드는 추가되지 않습니다.
답변3
user313992의 답변은 거의 최선의 접근 방식이며 적어도 다른 바이너리에서도 작동할 수 있습니다. 설명: gdb 시작, 출력 없음, pid에 추가, 플러시 수행, 암시적: 종료.
약간 개선하십시오.
gdb -batch -p $PID -ex 'call (int)fflush(stdout)' -ex 'call (int)fflush(stderr)'
이것의 장점은 올바른 반환 유형을 보장하고 아무것도 인쇄되지 않는다는 것입니다. 이 점을 염두에 두고 stderr 플러시도 추가했습니다.
이 방법은 다른 파일 핸들이 내부적으로 사용되거나 이러한 핸들이 GDB가 구문 분석할 수 없는 매크로인 경우 작동하지 않습니다.
자신만의 애플리케이션을 만드는 가장 좋은 방법은 다음과 같습니다.
extern
플러시를 수행하기 위한 모든 기능을 제공합니다 (call
위의 예에서 볼 수 있듯이 매크로 또는 다른 파일 핸들과 관련된 모든 문제를 제거하고 이미 GDB에서 편집 가능함)- 프로그램의 유용한 상태에서 이 함수를 호출하세요.
- 신호 처리기를 등록합니다
SIGUSR1
. 예를 들어 함수를 호출하면 해당 신호를 보내 강제로 새로 고칠 수 있습니다. - 파일이 존재하는지 폴링(존재하는 경우 삭제 후 새로 고침)
- 메시지/세마포어/소켓/...에 대한 폴링
- 신호 처리기를 등록합니다