【问题标题】:Weird behaviour using fork();使用 fork() 的奇怪行为;
【发布时间】:2017-03-03 00:20:09
【问题描述】:

我在 C 中有这个小程序

main(int argc, char **argv)
{
    forkit(4);
}
void forkit(int n)
{
if(n > 0)
    {
        fork();
        printf("%d \n", n);
        forkit(n-1);
    }
}

打印出来的

4 4 3 3 3 3 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

30 个数字,全部换行。但是,如果我在 printf 语句中删除 \n ,它会打印:

4 3 2 1 4 3 2 1 4 3 2 1 4 3 2 1 4 3 2 1 4 3 2 1 4 3 2 1 4 3 2 1 4 3 2 1 4 3 2 1 4 3 2 1 4 3 2 1 4 3 2 1 4 3 2 1 4 3 2 1 4 3 2 1

如果没有换行,它会给出 64 个数字。

这么小的变化怎么会产生如此不同的结果?

【问题讨论】:

  • 当我运行它时:fork > out.txt && wc -l out.txt -- 我得到 64(留下换行符)
  • 数字n 不在我认为的相同地址空间中。您创建的每个新流程(fork 所做的)都将是您流程的一个新实例。
  • @TomDignan 也一样,无论有没有换行,我都得到 64。
  • 这里有几点观察:(1)我不相信 printf 是线程安全的。您需要在此调用周围放置一个互斥锁。观察(2),每次 printf 遇到换行符都会强制刷新。这将改变你的时间特性。开始写入磁盘是切换线程的可靠方法。编辑:DigitalRoss 的解释更完整。请参考。
  • 似乎取决于缓冲模式,如果我用fflush 刷新缓冲区,即使没有新行(即进程数)我也会得到 30

标签: c fork


【解决方案1】:

非常好的问题, 发生了一些微妙的事情。

发生的情况是 printf 的输出被行缓冲并在换行处刷新。

就其本身而言,这不会影响大多数程序的输出,只会影响速度。

但是,当您分叉时,尚未输出的缓冲 I/O 现在出现在两个孩子中。使用换行符 in 输出已经发生,因此它不再在子进程中挂起。

这就是解释。如果不清楚,另一种说法是:没有换行符,待处理的输出乘以进程树中未来子节点的数量。使用换行符,它只是过去发生的输出,程序按您期望的方式运行。

请注意,即使没有换行符,输出最终也会被刷新,但它会在程序调用 exit(3). 时发生.

顺便说一句,如果您将输出重定向到一个文件,它将被块缓冲,并且在这两种情况下您可能会看到相似的结果(换行符除外)。

【讨论】:

  • 感谢您的回答。现在我只是不确定为什么它给出了 64 个数字(4 3 2 1 用于 16 个 n=1 的“叶”进程中的每一个)而不打印其他数字,例如 4 3 2 用于 n=2 的 8 个进程?
  • 注意:这就是为什么最好至少刷新stdoutstderr 通常是无缓冲的,所以无论如何都可以),理想情况下是刷新(或关闭)分叉之前所有其他打开的文件描述符。
  • 我只是想知道,这个问题可能是操作系统甚至平台相关的,我说得对吗?
  • @MateuszBartkowski fork 将每个进程一分为二;一半的叶子进程是树上一层的 8 个进程。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-12-02
  • 2020-09-03
  • 2016-04-30
  • 2016-02-19
  • 1970-01-01
  • 1970-01-01
  • 2019-12-04
相关资源
最近更新 更多