谁先叉,结果矛盾

时间:2018-12-03 19:38:30

标签: linux kernel fork scheduler

我有一个简单的测试:

int main() {
    int res = fork();
    if (res == 0) { // child
        printf("Son running now, pid = %d\n", getpid());
    }
    else { // parent
        printf("Parent running now, pid = %d\n", getpid());
        wait(NULL);
    }
    return 0;
}

当我运行一百次时,即运行此命令,

for ((i=0;i<100;i++)); do echo ${i}:; ./test; done

我得到:

0:
Parent running now, pid = 1775
Son running now, pid = 1776
1:
Parent running now, pid = 1777
Son running now, pid = 1778
2:
Parent running now, pid = 1779
Son running now, pid = 1780

,依此类推;而当我第一次写入文件然后然后读取文件时,即运行此命令,

for ((i=0;i<100;i++)); do echo ${i}:; ./test; done > forout
cat forout

我把它翻转了!也就是说,

0:
Son running now, pid = 1776
Parent running now, pid = 1775
1:
Son running now, pid = 1778
Parent running now, pid = 1777
2:
Son running now, pid = 1780
Parent running now, pid = 1779

我了解调度程序。对于谁在分叉之后先跑,这个结果并不意味着什么? 分叉功能do_fork()(位于kernel/fork.c)以将need_resched标志设置为1结束,内核开发人员在注释中说:“让子进程首先运行。”

我猜想这与printf写入的缓冲区有关。

还可以说输入重定向(>)首先将所有内容写入缓冲区,然后才将其复制到文件吗?即便如此,为什么还要更改打印顺序?

注意::我正在具有Linux内核 v2.4.14 单核虚拟机上运行测试。

谢谢您的时间。

1 个答案:

答案 0 :(得分:2)

当您重定向时,glibc检测到stdout不是tty会打开输出缓冲以提高效率。因此,直到进程退出后才写入缓冲区。您可以通过以下方式查看此信息:

int main() {
  printf("hello world\n");
  sleep(60);
}

以交互方式运行它时,它会打印“ hello world”并等待。重定向到文件时,您会在60秒内看不到任何内容:

$ ./foo > file & tail -f file
(no output for 60 seconds)

由于您的父进程正在等待子进程,因此它必然总是最后退出,因此最后刷新其输出。