我正在学习fork()
,我有一些问题。
请考虑以下代码:
#include <stdio.h>
#include <unistd.h>
int main()
{
int i;
for(i = 0; i < 5; i++)
{
printf("%d", i);
if((i%2)==0)
if(fork())
fork();
}
}
当我输出到终端时,我得到了我期望的结果(即:0,1,1,1,2,2,2,...
)。但是当我输出到文件时,结果完全不同:
案例1 :(输出到终端,例如:./a.out
):
结果是:0,1,1,1,2,2,2,...
案例2 :(输出到文件,例如:./a.out > output_file
)
结果是:0,1,2,3,4,0,1,2,3,4,0,1,2,3,4,...
为什么会这样?
答案 0 :(得分:6)
当您输出到文件时,stdio库会自动阻止缓冲出站位。
当程序调用{{1}}或从exit(2)
返回时,任何剩余的缓冲位都会被刷新。
在这样一个不会产生太多输出的程序中,当目标不是tty时,从main(),
返回后,将发生所有的I / O.这通常会改变I / O操作的模式和顺序。
在这种情况下,main(),
次调用会使结果更加复杂。这将复制每个子图像中部分填充和尚未刷新的I / O缓冲区。
在程序调用{{1}}之前,可以先使用fflush(3)刷新I / O.如果没有完成此刷新,那么您可能希望除了一个(通常是:子项)之外的所有进程都fork()
而不是fork(),
或从_exit(2)
返回以防止输出相同的位不止一次。 (_exit(2)只是执行退出系统调用。)
答案 1 :(得分:4)
程序中的fork()
if
块内执行两次,因为一旦fork成功,程序将由两个进程控制( child 和父进程)。fork()
块内的if
由子进程和父进程执行。因此它将具有与预期不同的输出,因为它由两个不同的过程控制,并且它们的执行顺序未知。即。在每个fork()
输出和文件之间的行为差异。这就是原因。
您写入缓冲区的内容(最终写入file(disk)
)无法保证写入文件(磁盘)immediatley。只有在执行 main() 完成后,才会将其刷新到磁盘。然而,在执行 main()期间,它会输出到终端。
在写入磁盘中的文件之前,kernel
实际上将数据复制到缓冲区中,稍后在后台,内核收集所有脏缓冲区,对它们进行最佳排序并将它们写入文件(磁盘) )。这称为回写。它还允许内核将写入推迟到更多空闲期间并批量处理多个写入。
为了避免这种行为,使用fork()
int pid;
if((pid = fork()) == -1 )
{ //fork unsuccessful
}
else if ( pid > 0)
{ //This is parent
}
else
{//This is child
}
答案 2 :(得分:2)
缓冲流有时会产生一些奇怪的结果......特别是当你有多个进程使用相同的缓冲流时。强制缓冲区为flushed,您会看到不同的结果:
int main()
{
int i;
FILE * fd = fopen(yourfile, "w");
for(i = 0; i < 5; i++)
{
fprintf(fd, "%d", i);
fflush(fd);
if((i%2)==0)
if(fork())
fork();
}
}
另外,为了您的调试目的,最好转储进程&#39; ID,这样您就可以看到哪个进程产生了哪些进程,并且更好地了解了进展情况。 getpid()
可以帮助您。
答案 3 :(得分:1)
为什么终端和文件之间的输出不同 分叉?
C标准库函数使用内部缓冲来加速。大多数实现使用完全缓冲的IO用于文件流,行缓冲用于stdin / stdout,而无缓冲用于stderr。
所以你的问题可以通过多种方式解决:
fflush(3)
setvbuf(3)
write(2)
代替stdlib的printf(3)
stderr
***** fprintf(3)
_exit(2)
退出,而不是exit(3)
****** 如果出现以下情况,最后两个可能无法正常工作:
*默认情况下,您的实现不使用对stderr的无缓冲写入(ISO C要求)
**你在child中编写了超过默认缓冲区大小的内容,并自动刷新了。
<强> PS 即可。同样,如果您需要更深入的标准库函数和缓冲知识,我建议您阅读Advanced Programming in the UNIX Environment (2nd Edition) by W. Richard Stevens and Stephen A. Rago。
<强> PPS 即可。顺便说一句,你的问题是一个非常受欢迎的C / C ++程序员职位面试问题。