줄 바꿈이 형식 문자열이 아닌 한 호출 후 printf가 플러시되지 않는 이유는 무엇입니까?


539

printf줄 바꿈이 형식 문자열이 아닌 한 호출 후 플러시 하지 않는 이유는 무엇 입니까? 이 POSIX 동작입니까? printf매번 어떻게 즉시 플러시 할 수 있습니까?


2
파일에서 발생하는지 아니면 터미널에서만 발생하는지 조사 했습니까? 내가 그것을 적용하지 않을 것으로 예상하지만 즉, 백그라운드 프로그램에서 출력되지 않은 라인에 영리 터미널 기능이 아닙니다로 들릴 겁니다 전경 프로그램입니다.
PypeBros 2009

7
Cygwin bash에서 줄 바꿈 형식 문자열에 있더라도 이와 동일한 오작동이 나타납니다 . 이 문제는 Windows 7의 새로운 기능입니다. 동일한 소스 코드가 Windows XP에서 제대로 작동했습니다. MS cmd.exe가 예상대로 플러시됩니다. 이 수정 setvbuf(stdout, (char*)NULL, _IONBF, 0)은 문제를 해결하지만 반드시 필요한 것은 아닙니다. MSVC ++ 2008 Express를 사용하고 있습니다. ~~~
Steve Pitchers

9
질문의 제목을 명확히하기 위해 : printf(..) 플러시 자체를 수행하지 마십시오stdout . 개행을 볼 때 플러시 될 수 있는 버퍼링입니다 (라인 버퍼링 된 경우). 같은 방식으로 반응 putchar('\n');하므로 printf(..)이와 관련하여 특별하지 않습니다. 이는 홍조에 cout << endl;대한 문서가 눈에 띄게 언급 된와 대조적입니다 . printf문서에는 플러싱에 대한 언급이 전혀 없습니다.
Evgeni Sergeev

1
쓰기 (플러싱)는 잠재적으로 비용이 많이 드는 작업이므로 성능상의 이유로 버퍼링 될 수 있습니다.
hanshenrik

@ EvgeniSergeev : 질문이 문제를 잘못 진단했으며 출력에 줄 바꿈이있을 때 플러시가 발생한다는 합의가 있습니까? (형식 문자열에 하나를 입력하는 것은 출력에 하나를 얻는 유일한 방법은 아니지만)입니다.
Ben Voigt

답변:


702

stdout스트림 라인은 줄 바꿈에 도달 한 후 (또는이 말했 때) 그래서에만 버퍼에 무엇이 표시됩니다, 기본적으로 버퍼링된다. 즉시 인쇄 할 수있는 몇 가지 옵션이 있습니다.

로 인쇄 stderr대신에 사용하는 것은 fprintf( stderr이다 기본적으로 버퍼링 )

fprintf(stderr, "I will be printed immediately");

필요할 때마다 stdout을 플러시하십시오 fflush.

printf("Buffered, will be flushed");
fflush(stdout); // Will now print everything in the stdout buffer

편집 : 아래 Andy Ross의 의견에서 다음을 사용하여 stdout에서 버퍼링을 비활성화 할 수도 있습니다 setbuf.

setbuf(stdout, NULL);

또는 여기에setvbuf 설명 된 보안 버전

setvbuf(stdout, NULL, _IONBF, 0); 

266
또는 버퍼링을 완전히 비활성화하려면setbuf(stdout, NULL);
Andy Ross

80
또한 UNIX에서 개행은 일반적으로 stdout이 터미널 인 경우에만 버퍼를 플러시한다는 점을 언급하고 싶었습니다. 출력이 파일로 리디렉션되는 경우 줄 바꿈이 플러시되지 않습니다.
hora

5
나는이 이론을 테스트 setlinebuf()하고 있으며 터미널 향하지 않는 스트림에서 사용하는 것이 각 라인의 끝에서 플러시되고 있음을 발견했습니다.
Doddy

8
"처음에 열었을 때 표준 오류 스트림은 완전히 버퍼링되지 않습니다. 표준 입력 및 표준 출력 스트림은 스트림이 대화식 장치를 참조하지 않는 것으로 결정될 수있는 경우에만 완전히 버퍼링됩니다"-이 질문을 참조하십시오 : stackoverflow.com / questions / 5229096 /…
Seppo Enarvi

3
@RuddZwolinski 이것이 왜 "인쇄되지 않습니까?"라는 정식 답변이 되려면 "printf는 항상 줄 바꿈 발생시 버퍼를 플러시합니까?"에 따라 터미널 / 파일 구별을 언급하는 것이 중요합니다. 이 높은 평가를받은 답변에서 직접 의견을 읽어야하는 사람들과 비교하여 ...
HostileFork는 22:08의 SE

128

아니요, POSIX 동작이 아니며 ISO 동작 입니다. 만하는 한 그들은 ISO에 부합로 POSIX 동작).

표준 출력은 대화식 장치를 나타내는 것으로 감지 될 수있는 경우 라인 버퍼링됩니다. 그렇지 않으면 완전히 버퍼링됩니다. 따라서 다음 printf과 같이 줄 바꿈을 보내더라도 플러시되지 않는 상황이 있습니다 .

myprog >myfile.txt

사용자와 상호 작용하는 경우 모든 행을보고 싶을 수 있으므로 효율성에 적합합니다. 출력을 파일로 보내는 경우 다른 쪽 끝에 사용자가 없을 가능성이 높습니다 (불가능하지는 않지만 파일을 테일링 할 수 있음). 이제 당신 수 있습니다 사용자가 모든 캐릭터를보고 싶어하지만 두 가지 문제 있다고 주장 있습니다.

첫 번째는 매우 효율적이지 않다는 것입니다. 두 번째는 원래의 ANSI C 명령이 새로운 행동을 발명하기보다는 기존의 행동 을 주로 체계화 하는 것이며, 그러한 설계 결정은 ANSI가 프로세스를 시작하기 훨씬 전에 이루어 졌다는 것입니다. 오늘날의 ISO조차도 표준의 기존 규칙을 변경할 때 매우 신중하게 밟습니다.

그걸 다루는 방법에 관해서는 fflush (stdout) 즉시보고 싶은 모든 출력 호출 후에 문제를 해결할 것입니다.

또는에서 setvbuf작동하기 전에 stdout버퍼링되지 않도록 설정하고 fflush코드에 모든 행을 추가하는 것에 대해 걱정할 필요가 없습니다 .

setvbuf (stdout, NULL, _IONBF, BUFSIZ);

출력을 파일 보내는 경우 성능에 상당한 영향을 줄 수 있음을 명심 하십시오. 또한 이것에 대한 지원은 표준에 의해 보장되지 않고 구현에 의해 정의된다는 것을 명심하십시오.

ISO C99 섹션 7.19.3/3은 관련 비트입니다.

스트림이 버퍼링되지 않은 경우 가능한 빨리 소스 또는 대상에서 문자가 표시됩니다. 그렇지 않으면 문자가 블록으로 호스트 환경에 쌓이거나 전송 될 수 있습니다.

스트림이 완전히 버퍼링 되면, 버퍼가 채워질 때 문자가 호스트 환경으로 또는 블록에서 호스트로 전송됩니다.

스트림이 라인 버퍼링 되는 경우, 개행 문자가 발생할 때 문자가 호스트 환경과 블록으로 전송되도록 의도됩니다.

또한 문자는 버퍼가 채워질 때, 버퍼되지 않은 스트림에서 입력이 요청 될 때 또는 호스트 환경에서 문자를 전송해야하는 라인 버퍼링 된 스트림에서 입력이 요청 될 때 호스트 환경에 블록으로 전송됩니다. .

이러한 특성에 대한 지원은 구현에 따라 정의되며 setbufsetvbuf기능 을 통해 영향을받을 수 있습니다 .


8
방금 '\ n', printf ()가 플러시되지 않는 시나리오를 보았습니다. 여기서 언급했듯이 fflush (stdout)를 추가하여 극복했습니다. 그러나 '\ n'이 printf ()에서 버퍼를 플러시하지 못한 이유가 궁금합니다.
Qiang Xu

11
@ QiangXu, 표준 출력은 대화식 장치를 참조하도록 결정될 수있는 경우에만 라인 버퍼링됩니다. 예를 들어,로 출력을 리디렉션하면 myprog >/tmp/tmpfile라인 버퍼가 아닌 완전히 버퍼링됩니다. 메모리에서 표준 출력이 대화 형인지에 대한 결정은 구현에 맡겨집니다.
paxdiablo

3
또한 윈도우 전화 setvbuf에 (..., _IOLBF는) _IOLBF로하지 작업이 _IOFBF과 동일합니다 : msdn.microsoft.com/en-us/library/86cebhfs.aspx
표트르 Lopusiewicz

28

아마도 효율성과 단일 TTY에 여러 프로그램을 작성하는 경우이 방법으로 인터레이스 된 줄에 문자가 표시되지 않기 때문일 수 있습니다. 따라서 프로그램 A와 B가 출력되면 일반적으로 다음을 얻습니다.

program A output
program B output
program B output
program A output
program B output

이 냄새가 나지만,

proprogrgraam m AB  ououtputputt
prproogrgram amB A  ououtputtput
program B output

개행 문자를 플러시한다고 보장되지는 않으므로 플러시가 중요한 경우 명시 적으로 플러시해야합니다.


26

즉시 다량 전화로 fflush(stdout)또는 fflush(NULL)( NULL수단 플러시 다).


31
염두에 두어야은 fflush(NULL);일반적으로 아주 나쁜 생각이다. 많은 파일을 열면 특히 잠금을 위해 모든 것과 싸울 멀티 스레드 환경에서 성능이 저하됩니다.
R .. GitHub 중지 지원 얼음

14

참고 : Microsoft 런타임 라이브러리는 라인 버퍼링을 지원하지 않으므로 다음을 수행하십시오 printf("will print immediately to terminal").

https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/setvbuf


3
보다 더 printf"정상적인"경우에 단자에 바로 갈는 사실이다 printffprintf더 거칠게 자신의 출력은 즉시 사용을 넣어 경우에도 버퍼 얻을. MS가 문제를 해결하지 않으면, 한 프로그램이 다른 프로그램에서 stderr과 stdout을 캡처하고 각각에 전송 된 순서를 식별 할 수 없습니다.
supercat

버퍼링이 설정되어 있지 않으면 터미널에 즉시 인쇄되지 않습니다. 기본적으로 전체 버퍼링이 사용됩니다
phuclv

12

stdout은 버퍼링되므로 개행이 인쇄 된 후에 만 ​​출력됩니다.

즉시 출력하려면 다음 중 하나를 수행하십시오.

  1. stderr로 인쇄하십시오.
  2. stdout을 버퍼링하지 마십시오.

10
또는 fflush(stdout).
RastaJedi

2
"따라서 줄 바꿈이 인쇄 된 후에 만 ​​출력됩니다." 이뿐 만 아니라 다른 경우는 4 개 이상입니다. buffer full, write to stderr(이 답변은 나중에 언급) fflush(stdout),, fflush(NULL).
chux-Reinstate Monica

11

기본적으로 stdout은 라인 버퍼링되고 stderr은 버퍼링되지 않으며 파일은 완전히 버퍼링됩니다.


10

fprintf를 stderr로 버퍼링 할 수 있습니다. 또는 원할 때 stdout을 플러시 할 수 있습니다. 또는 stdout을 unbuffered로 설정할 수 있습니다.



2

일반적으로 2 가지 레벨의 버퍼링이 있습니다.

1. 커널 버퍼 캐시 (읽기 / 쓰기 속도 향상)

2. I / O 라이브러리의 버퍼링 (시스템 호출 수 감소)

의 예를 들어 보자 fprintf and write().

을 호출 fprintf()하면 파일로 직접 전달되지 않습니다. 먼저 프로그램 메모리의 stdio 버퍼로갑니다. 거기에서 쓰기 시스템 호출을 사용하여 커널 버퍼 캐시에 기록됩니다. 따라서 I / O 버퍼를 건너 뛰는 한 가지 방법은 write ()를 직접 사용하는 것입니다. 다른 방법은을 사용하는 것 setbuff(stream,NULL)입니다. 버퍼링 모드가 버퍼링 없음으로 설정되고 데이터가 커널 버퍼에 직접 기록됩니다. 데이터를 커널 버퍼로 강제로 이동시키기 위해 기본 버퍼링 모드 인 '라인 버퍼링'의 경우 I / O 버퍼를 플러시하는 "\ n"을 사용할 수 있습니다. 또는 사용할 수 있습니다 fflush(FILE *stream).

이제 우리는 커널 버퍼에 있습니다. 커널 (/ OS)은 디스크 액세스 시간을 최소화하려고하므로 디스크 블록 만 읽거나 씁니다. 따라서 a read()가 발행되면 시스템 호출이며 직접 또는 통해 호출 할 수 있습니다fscanf() 커널은 디스크에서 디스크 블록을 읽고이를 버퍼에 저장합니다. 그 후 데이터가 여기에서 사용자 공간으로 복사됩니다.

마찬가지로 fprintf()I / O 버퍼에서받은 데이터는 커널에 의해 디스크에 기록됩니다. 이렇게하면 read () write ()가 더 빨라집니다.

커널이 write()하드웨어 컨트롤러에 의해 데이터 전송을 제어 한 후 시작하도록 강제하기 위해 몇 가지 방법이 있습니다. O_SYNC쓰기 호출 중에 비슷한 플래그를 사용할 수 있습니다 . 또는 fsync(),fdatasync(),sync()커널 버퍼에서 데이터를 사용할 수있게되면 커널이 쓰기를 시작하도록하는 것과 같은 다른 기능을 사용할 수 있습니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.