프로그램 1에서는 Hello world
한 번만 인쇄되지만 \n
삭제하고 실행하면(프로그램 2) 출력이 8번 인쇄됩니다. 여기의 중요성 \n
과 그것이 어떤 영향을 미치는지 누군가 나에게 설명해 줄 수 있습니까 fork()
?
계획 1
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("hello world...\n");
fork();
fork();
fork();
}
출력 1:
hello world...
시나리오 2
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("hello world...");
fork();
fork();
fork();
}
출력 2:
hello world... hello world...hello world...hello world...hello world...hello world...hello world...hello world...
답변1
C 라이브러리 printf()
함수를 사용하여 표준 출력으로 출력할 때 출력은 일반적으로 버퍼링됩니다. 개행 문자를 출력하거나, call 을 fflush(stdout)
출력하거나, 프로그램을 종료할 때까지( _exit()
호출을 통해서는 아님) 버퍼가 플러시되지 않습니다. 라인 버퍼링은 표준 출력 스트림이 TTY에 연결될 때 기본적으로 이러한 방식으로 수행됩니다.
"프로그램 2"에서 프로세스를 분기하면 하위 프로세스는 플러시되지 않은 출력 버퍼를 포함하여 상위 프로세스의 모든 부분을 상속합니다. 이는 플러시되지 않은 버퍼를 각 하위 프로세스에 효과적으로 복사합니다.
프로세스가 종료되면 버퍼가 플러시됩니다. 원래 프로세스를 포함하여 총 8개의 프로세스를 시작하면 플러시되지 않은 버퍼는 각 프로세스가 종료될 때 플러시됩니다.
그것은여덟매번 fork()
이전보다 두 배 많은 프로세스를 얻고 (무조건적이기 때문에) 그 중 3개(2 3 = 8)를 fork()
갖게 되기 때문입니다.
답변2
어떤 식으로든 포크에 영향을 미치지 않습니다.
첫 번째 경우에는 (으로 인해) 출력 버퍼가 비워졌기 때문에 쓸 내용이 없는 8개의 프로세스로 끝납니다 \n
.
두 번째 경우에는 여전히 8개의 프로세스가 있고 각 프로세스에는 "Hello world..."가 포함된 버퍼가 있으며 이 버퍼는 프로세스가 끝날 때 기록됩니다.
답변3
@Kusalananda는 출력이 다음과 같은 이유를 설명했습니다.반복하다. 왜 출력이 반복되는지 궁금하시다면8회그리고 단지 4번(기본 프로그램 + 브랜치 3개)이 아닙니다.
int main()
{
printf("hello world...");
fork(); // here it creates a copy of itself --> 2 instances
fork(); // each of the 2 instances creates another copy of itself --> 4 instances
fork(); // each of the 4 instances creates another copy of itself --> 8 instances
}
답변4
여기서 중요한 배경은 stdout
필요성 이다.라인 버퍼기본 설정으로 표준을 누릅니다.
이로 인해 \n
출력이 플러시됩니다.
두 번째 예에는 개행 문자가 포함되어 있지 않으므로 출력이 플러시되지 않으며 fork()
전체 프로세스를 복사하므로 버퍼 상태도 복사됩니다 stdout
.
이제 fork()
예제의 이러한 호출은 총 8개의 프로세스를 생성하며 모두 버퍼 상태의 복사본을 포함합니다 stdout
.
정의 에 따르면 이러한 모든 프로세스 exit()
는main()
exit()
fflush()
fclose()
표준 입력 및 출력개울. 여기에는 이 포함되므로 stdout
동일한 콘텐츠가 8번 표시됩니다.
좋은 방법은 fflush()
를 호출하기 전에 보류 중인 출력이 있는 모든 스트림을 호출 fork()
하거나 분기된 하위 프로세스를 명시적으로 호출하도록 하여 _exit()
stdio 스트림을 플러시하지 않고 프로세스만 종료하는 것입니다.
호출은 stdio 버퍼를 플러시하지 않으므로 (호출 후) call 및 (실패한 경우) call 하면 exec()
stdio 버퍼에 신경 쓸 수 없습니다.fork()
exec()
_exit()
참고: 잘못된 버퍼링으로 인해 발생할 수 있는 문제에 대한 아이디어를 얻으려면 최근 수정된 Linux 버그가 있습니다.
표준에서는 stderr
기본적으로 버퍼링이 필요하지 않지만 Linux는 이를 무시하고 stderr
라인 버퍼링과 stderr이 파이프를 통해 리디렉션되는 경우 전체 버퍼링을 수행합니다. 따라서 UNIX용으로 작성된 프로그램이 Linux에서 개행 없이 출력되기에는 너무 늦습니다.
아래 댓글을 보니 지금은 수정된 것 같습니다.
이 Linux 문제를 해결하기 위해 다음과 같이 했습니다.
/*
* Linux comes with a broken libc that makes "stderr" buffered even
* though POSIX requires "stderr" to be never "fully buffered".
* As a result, we would get garbled output once our fork()d child
* calls exit(). We work around the Linux bug by calling fflush()
* before fork()ing.
*/
fflush(stderr);
fflush()
방금 플러시된 스트림을 호출하는 것은 아무 작업도 하지 않으므로 이 코드는 다른 플랫폼에 해를 끼치지 않습니다 .