Fil*_*cki 2 c linux concurrency fork
朋友给我发了个程序:
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
void chain(int n, int current_nr) {
printf("Running %d\n", current_nr);
pid_t pid = fork();
if (pid != 0) { //parent
return;
}
else { //child
if (current_nr == n - 2) {
return;
}
else {
chain(n, current_nr + 1);
}
}
}
int main(void) {
puts("Program started");
chain(5, 0);
wait(NULL);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
如果程序以./program形式执行,则输出为:
Program started
Running 0
Running 1
Running 2
Running 3
Run Code Online (Sandbox Code Playgroud)
但在执行./program> outputfile之后,输出文件内容为:
Program started
Running 0
Running 1
Running 2
Running 3
Program started
Running 0
Running 1
Running 2
Running 3
Program started
Running 0
Running 1
Running 2
Program started
Running 0
Running 1
Program started
Running 0
Run Code Online (Sandbox Code Playgroud)
到底是怎么回事?
背景:在类Unix操作系统中,C标准库检测输出是否发送到终端,或者是否将其重定向到文件或管道.在终端上,stdout输出是行缓冲的,这意味着在打印换行符时会刷新它'\n'.当它转到文件/管道时,stdout默认情况下是完全缓冲的,这意味着它只在缓冲区已满时刷新(或者stdout关闭,通常在程序退出时).
更多背景:第三种缓冲模式是无缓冲,这意味着您打印的所有内容都会立即发送到内核.stdout(隐式使用printf)是一个FILE*写入进程标准输出的全局变量,这里的缓冲只是对所有FILE*文件进行的正常缓冲,唯一特殊的是默认缓冲模式"魔术".出于性能原因,存在不同的缓冲模式.缓冲区刷新意味着系统调用以及在后台开始发生的各种事情.当数据以尽可能大的块发送到内核时,可能的开销最小.当打印到终端时,行缓冲允许用户查看文本,而在写入文件时,完全缓冲最大化吞吐量.此外,stdout缓冲区在读取之前被刷新stdin,这就是为什么代码在printf("Prompt: " /* no newline */); fgets(...);不改变缓冲模式或显式刷新的情况下工作的原因.
回答问题:因此,当完全缓冲并打印第一行时,它会保留在进程自己的缓冲区中.然后你fork,缓冲区被复制到子进程.这种情况会发生几次,也适用于其他输出线.这就是你多次获得相同输出的原因:当你分叉时它仍然在父进程缓冲区中.然后当进程退出时,它们的缓冲区会立即全部刷新,因此您可以将每个进程的输出作为一个整体输出.这里你打印得很少,所有东西都适合缓冲区,所以由于缓冲区已满而没有输出.这就是你输出如此干净的原因.如果由于缓冲区已满而中间存在刷新,则输出会更加混乱,您可以通过设置小缓冲区大小的完全缓冲来测试,例如7.
如何修复:您可以使用setvbuf标准C功能更改缓冲模式,以进行行缓冲或无缓冲.您还可以fflush在执行之前使用标准C函数进行显式刷新fork(并且您的程序很好地证明了在分叉时需要处理整个问题).