奇怪的fork结果
本文最后更新于:2023年12月17日 下午
前言
最近在上操作系统的相关课程,恰好学到了fork函数。这么为了引入问题,简单测试一下,请问下面的代码会输出什么?
1 |
|
奇妙的过程
相信聪明的小伙伴,马上就能回答,“会输出8行 Hello World”。原因也很简单,因为每次执行完fork函数都会生成一个跟父进程一样的子进程,所以第一次执行完fork,总共有2个进程,第二次执行完fork就会有4个,第三次执行完就会有8个,所以最后每个进程都会输出一行Hello World。
接下来,我们稍微改变代码,变成下面这样子,那么这次输出结果会怎么样呢?
1 |
|
这不也还是很简单吗,结果只是输出一行“Hello World”,因为打印操作在fork操作前,所以接下来的子进程都不会再执行打印输出的操作了。一开始我也是这么想的,但是VScode上的输出结果却令人大吃一惊,这怎么还是输出8行啊?
这真让人有些摸不着头脑,然后我又试了试在命令行下直接编译执行,发现这次得到的结果符合预期,这更加令人疑惑了。所以我又去试了试在Clion下执行这段代码,发现输出的结果也是符合预期的。
那么在多方对比之下,只能是认为是VScode出了问题。当时我还认为是VScode里Code Runner这个插件的问题,所以在Stack Overflow上提问了[1],随后得到了大牛的解答。
简单来说printf是有一个buffer的,也就是说并不是执行完printf语句,就会立即输出显示到终端屏幕(标准输出stdout)上,只有buffer满了,或者显示调用刷新,或是输入回车符 ‘\n’ ,才会立即将缓冲区的内容输出到屏幕上。
但是对于VScode来说,程序代码并不是直接运行在终端shell里的,标准输出stdout会绑定到IDE自身的一个GUI(图形用户界面)。此时,只有当缓冲区满了,或者是手动刷新缓冲区的时候,才会立刻输出到GUI界面上,也就是说缓冲区接收到了’\n’回车符也不会立即输出。下图是我的理解,可能不太准确,不过大体上这么解释能够说得通。
因此就有了输出8行的奇怪形象,因为每个子进程都继承了缓冲区里的内容,直到最后程序退出时刷新缓冲区,因此最后每个程序都输出一行。
然后在尝试下添加fflush
手动刷新缓冲区,输出果然变成了只有一行,验证了之前的想法。
1 |
|
小结
至此,有关fork之后奇怪的输出结果终于水落石出,其中涉及到的一个关键点就是printf或者说stdout是有缓冲区的,并不是执行完该函数语句就会立刻输出到屏幕上,这一个知识点其实以前也了解过,但是没有重视,或者说没有进一步了解底层实现,只知道个概念,所以真正遇到问题时,还是不能想到这一点。
所以还是要看源码啊,源码之前,了无秘密。但尴尬的是,我在IDE里想看对应库函数的源代码实现时,总是跳转不到对应的实现,只有一些声明,这就很尴尬。不知道有没有人跟我有类似的苦恼,正好等下次解决了,又可以水一篇博文了 :-)。