2013-01-22 58 views
4

我正在学习与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

“完全不同”是什么意思?你了解多处理,多个进程在同一时间工作吗? –

+0

不同的结果是什么? – imreal

+0

我们需要更多的代码。你如何写你的文件?文件上显示的输出是什么? – Grambot

回答

6

当您输出到文件时,stdio库会自动阻塞出站位。

当某个程序调用exit(2)或从main(),返回时,任何剩余的缓冲位都将被刷新。

在这样不会产生太大的输出方案,的I/O时的目的地不是一个tty从main(),回归之后会出现的所有。这通常会自行改变I/O操作的模式和顺序。

在这种情况下,结果是由fork()系列调用进一步复杂。这将复制每个子图像中的部分填充和尚未刷新的I/O缓冲区。

在程序调用fork(),之前,可能会先使用fflush(3)来刷新I/O。如果未执行此刷新,则可能需要除了一个(通常是子项)以外的所有进程到_exit(2)而不是exit(3)或从main(),返回,以防止相同的位输出超过一次。 (_exit(2)少了点exit系统调用)。

+0

我可以在哪里找到更多关于你解释事情的详细信息。提前致谢。 – akmal

+1

'man 2 _exit;男人2叉;男子3出口;男人3 stdio; man 3 fflush',也许http://www.yolinux.com/TUTORIALS/ForkExecProcesses.html – DigitalRoss

4

fork()if块在你的程序中执行两次,因为一旦叉是成功的,该程序是由两个过程(孩子控制父进程).So 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 
} 
+0

你能否澄清一下'fork'里面'if'执行两次。我认为child不会执行'fork'它会收到'fork'的结果,并且在'fork'之后的第一条语句中开始执行。 – akmal

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(); 
    } 
} 

此外,为您调试的目的,它可能是不错的转储进程的ID,这样你可以看到哪个进程产卵其中,并有更好地了解正在发生的事情。 getpid()可以帮助你。

1

为什么我在 分叉时在终端和文件之间有不同的输出?

C标准库函数使用内部缓冲加快速度。大多数实现将完全缓冲的IO用于文件流,行缓冲用于标准输入/标准输出和无缓冲用于标准错误。叉前

  1. 使用明确的缓冲区刷新通过fflush(3)
  2. 设置缓冲区通过setvbuf(3)
  3. 使用write(2),而不是STDLIB的printf(3)
  4. 手动键入:

    所以你的问题可以用许多方式解决输出到stderr默认通过fprintf(3)*

  5. 在派生的进程,而不是
  6. 退出与_exit(2)**

最后两位可能无法按预期工作,如果:
*您实现不使用默认的无缓冲写至标准错误(这是由ISO要求C)
**您已经在孩子中写入了超过默认缓冲区大小,并且是否自动刷新。

PS。再次,如果您需要更深入的标准库函数和缓冲知识,我推荐阅读Advanced Programming in the UNIX Environment (2nd Edition) by W. Richard Stevens and Stephen A. Rago

PPS。顺便说一句,你的问题是一个非常受欢迎的C/C++程序员职位面试问题。

相关问题