gpt4 book ai didi

C 并发程序输出取决于输出到标准输出还是文件

转载 作者:太空狗 更新时间:2023-10-29 11:27:06 27 4
gpt4 key购买 nike

friend 给我发了一个程序:

#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;
}

如果程序作为 ./program 执行,则输出为:

Program started
Running 0
Running 1
Running 2
Running 3

但是执行 as ./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

这是怎么回事?

最佳答案

背景:在类 Unix 操作系统中,C 标准库检测输出是否进入终端或者是否被重定向到文件或管道。在终端上,stdout 输出是行缓冲的,这意味着当您打印新行字符 '\n' 时它会被刷新。当它转到文件/管道时,stdout 默认情况下是完全缓冲的,这意味着它仅在缓冲区已满(或 stdout 关闭,通常在程序退出时)时刷新。

更多背景:第三种缓冲模式是无缓冲,这意味着您打印的所有内容都会立即发送到内核。 stdout(被printf隐式使用)是一个全局的FILE*变量,写入进程的标准输出,这里的缓冲只是普通的缓冲对所有 FILE* 文件都完成了,唯一特别的是默认的缓冲模式“magic”。出于性能原因,存在不同的缓冲模式。 Buffer flush 意味着系统调用和各种事情开始在后台发生。当数据以尽可能大的 block 发送到内核时,开销可能最少。当打印到终端时,行缓冲允许用户看到文本,而当写入文件时,全缓冲最大化吞吐量。此外,stdout 缓冲区在从 stdin 读取之前被刷新,这就是为什么代码像 printf("Prompt: "/* no newline */); fgets(...); 无需更改缓冲模式或显式刷新即可工作。

问题的答案: 因此,当完全缓冲时,您打印第一行时,它保留在进程自己的缓冲区中。然后你 fork,缓冲区被复制到子进程。这种情况会发生几次,也适用于其他输出线。这就是您多次获得相同输出的原因:当您 fork 时,它仍在父进程缓冲区中。然后当进程退出时,它们的缓冲区会立即全部刷新,因此您可以将每个进程的输出作为一个整体获得。在这里你打印的很少,所有东西都适合缓冲区,所以由于缓冲区已满而没有输出。这就是为什么您的输出如此干净的原因。如果由于缓冲区已满而在两者之间进行刷新,则输出会更加困惑,您可以通过设置具有较小缓冲区大小(例如 7)的完全缓冲来测试。

如何修复:您可以使用 setvbuf 更改缓冲模式标准 C 函数,行缓冲或无缓冲。您还可以使用 fflush 显式刷新在执行 fork 之前的标准 C 函数(并且您的程序很好地演示了在 fork 时需要处理整个问题)。

关于C 并发程序输出取决于输出到标准输出还是文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33553858/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com