fork()와 redirection in linux
글쓴이: mrx@Google / 작성시간: 금, 2019/10/18 - 12:41오전
#include
#include
#include
int main(int argc,char* argv[]){
pid_t pid;
printf("Hello! %d\n", getpid());
if((pid=fork())) {
waitpid(pid,0,0);
}
printf("Bye! %d\n",getpid());
return 0;
}
결과를 stdout으로 출력하면
Hello! parent id
Bye! child id
Bye! parent id
가 나오는데 다른 txt파일로 redirection 시키면
Hello! parent id
Bye! child id
Hello! parent id//추가됨
Bye! parent id
왜 Hello! parent id 가 뒤에 다시 출력 될까요? 알쏭달쏭하네요.
Forums:
서프라이즈!
1. syscall은 비쌉니다.
printf 한 번 할 때마다 write syscall을 호출하는 건 정말 비싸죠.
진짜 문제는, write syscall의 비용이 실제로 쓰는 문자 수에 꼭 비례하지는 않는다는 겁니다. 고정 비용이 있죠.
따라서 c 표준 라이브러리는 대개 출력을 버퍼링하게 되어 있습니다. 출력할 데이터를 모아 뒀다가 write 한 번으로 내보내는 (flush) 것이죠.
2. c 표준 라이브러리의 버퍼링 정책은 출력이 콘솔인지 파일인지에 따라 달라집니다.
버퍼링을 언제까지고 할 수는 없으니, 사용자의 명시적인 명령(fflush)이 없더라도 어느 시점에서는 flush해야 합니다. 그 "시점"이 문제인데,
1) 콘솔 출력의 경우, 표준 라이브러리는 사용자가 실시간으로 출력을 확인하고 반응해야 한다고 생각합니다. 따라서 버퍼를 더 자주 flush합니다. 특히 개행 문자('\n')가 출력되면 버퍼를 flush하게 되죠.
2) 파일 출력의 경우, 어차피 파일에 내용이 모두 쓰인 뒤에 나중에 한번에 확인하는 경우가 많기 때문에 표준 라이브러리는 좀 더 느긋해집니다. 버퍼가 좀 더 많이 쌓인 뒤에야 flush를 한다는 말입니다.
본문의 프로그램처럼 출력되는 내용이 많지 않을 경우에는, 프로그램이 종료되기 직전쯤에야 flush될 거라고 예상할 수 있습니다.
3. 본문의 프로그램이 왜 저렇게 동작하는지는 그런 의미에서 이해할 수 있죠.
1) 콘솔 출력의 경우, fork 전의 printf 출력은 개행 문자가 끝에 있으므로 거의 바로 flush됩니다. 즉 fork 전에 stdout의 버퍼가 비워진 상태이므로 예상대로 동작합니다.
2) 파일 출력의 경우, fork 전의 printf 출력은 아직 버퍼에 남아 있는 상태입니다. fork 직후에 child는 이전에 printf 되었던 내용이 담긴 버퍼를 고스란히 복사해서 가져갑니다. 그리고 parant와 child 모두 종료되기 전에 버퍼를 flush하므로, 결국 해당 내용은 두 번 출력되는 것이죠.
4. 이런 현상을 막을 수 있는 가장 간단한 방법은, fork 직전에 fflush를 호출해서 명시적으로 버퍼를 비워 주면 됩니다.
https://en.cppreference.com/w/c/io/fflush
댓글 달기