日期:2014-05-16 浏览次数:20908 次
下面的程序打印出多少个“*“ (小弟今年遇到的腾讯一面面试题,据说其他公司的面试题中也有这个题目)
#include <unistd.h> #include <stdio.h> int main() { for (int i = 0; i < 2; i++) { fork(); printf("*"); } return 0; }
答案:打印出8个*,而不是6个。
分析:
如果不考虑IO机制,一般可能会想,i = 0时,fork后有两个进程,打印两个*,i=1,时有四个进程,再打印4个*。所以一共6个。其实不是这样的。
进程机制:
fork分叉后子进程似乎父进程的副本,子进程会复制父进程的数据空间、堆和栈。而共享代码段。
C语言标准IO的缓冲机制:
C语言的标准IO是带缓冲的,一般调用printf后,并不是立即把要打印的内容立即打印在控制台界面上,而是输出到一个缓冲区,对应标准输出的叫标准输出缓冲,对应标准出错的叫标准出错缓冲,当然还有标准输入缓冲。
缓冲机制:
缓冲机制一般分为:全缓冲、行缓冲、无缓冲。
上面三种我们都能用flush和关闭文件之类的函数强制刷新缓冲。C语言还提供了接口让我们改变默认的缓冲机制,以及缓冲区的大小。
回到我们的问题,根据linux进程机制,题目中的标准输出文件描叙符也是会被子进程复制的。所以fork后所有的进程共享一个控制台窗口(这个解释好像有点多余,看不懂跳过)。IO的缓冲区是用malloc申请的,是属于堆区,所以也是子进程要从父进程复制的。那么i= 0时,有两个进程,他们各自输出了一个*,而且由于标准输出默认采用的是行缓冲机制,所以此时,它们只是各自把一个*复制到了标准输出的缓冲区,没有打印到c控制台窗口上。 接下来是关键时刻,接下来 i = 1,再一个运行到fork()后,子进程复制父进程的堆区,所以后面出生的子进程也和父进程有着一样的标准输出缓冲区,而且标准输出缓冲区中同样也有着一个*。然后运行大printf语句,所有的进程都又各自向自己的标准输出缓冲区输送了一个*。 然后
i=2,程序结束了,标准输出缓冲区的内容打印到控制台窗口上,你就看到几个*了。
好的,我们现在统计一下。每个进程都一个输出了几个*。
我们把进程按照出生的先后分为两拨,第一波是i = 0时就出现的,包括主线程和第一个字进程,在整个循环中他们向自己的标准输出缓冲区都输出了两个*。
第二波是在i = 1时产生的两个子进程,它们各自像自己的标准输出缓冲区输出了一个*,但是由于它们从父进程的缓冲区里复制了一个*,所以它们的标准输出缓冲区中都有两个*。
最后程序结束,关闭标准输出文件时,IO发生,一共向控制台窗口打印了:两个x两个 + 两个x两个 = 8个 *。
运行结果截图:
如果把程序中的printf语句中加上一个\n符号,改为printf("*\n"),就是下面的结果,打印出了6个*。