问题一: 下面程序一共输出多少个"-"
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
int i;
for(i=0; i<2; i++){
fork();
printf("-");
}
return 0;
}
按照fork()机制的运行逻辑,其输出应该是6个"-",但是这段程序的输出结果是8个"-"
在弄清楚这个问题之前,需要了解fork()系统调用的几个特性:
- fork()系统调用是用于创建子进程的系统调用,一次调用,两次返回(大家可以分析一下fork系统调用的汇编代码,看它是如何实现两次返回的),如果返回0,则是子进程;如果返回值大于0,则是父进程(返回值是子进程的pid)
- 还需要注意的一个细节就是:在fork()系统调用处,真个父进程的执行环境会全部复制给子进程,其中包括指令、变量值、程序调用栈、环境变量、缓冲区等。
上面的执行结果之所以会输出8个"-",就是因为printf("-")语句是带有buffer的,printf("-")会将"-"放到该进程的buffer中,当进行fork()系统调用时,自己程会复制父进程buffer中的内容,从而多了两个"-"
另外,linux中的设备分为块设备和字符设备,一般情况下块设备是有缓冲的,而字符设备是没有缓冲的。
如果将上述代码修改为:
printf("-\n") ;
或是
printf("-") ;
fflush(stdout) ;
最终的输出结果就是6个,因为程序遇到"\n",或是EOF,或是缓冲区满,或是文件描述符关闭,或是主动flush,或是程序退出,就会把数据刷出缓冲区。由于标准输出是行缓冲,所以遇到"\n"时会刷出缓冲,而对于磁盘这个块设备来说,"\n"并不会引起缓冲区刷出操作, 因为磁盘是全缓冲,可以使用setvbuf来设置缓冲区大小,或是用fflush刷新缓存。因此如果将程序的输出重定向到文件的话,第一种代码修改方案依旧会输出8个"-",而第二种方案会正常输出6个
下面以图的形式展现整个fork的过程:
上图中相同颜色的表示同一个进程,使用pstree -p | grep fork命令可以打印出进程之间的关系,如下图所示:
这样,对于printf("-");这样的语句,我们就可以清楚的知道,那个子进程复制到父进程缓冲区的内容而导致多次输出了。如下图所示,加了阴影和双边框的两个子进程导致了多次输出
问题二:下面代码的字符串是一起输出还是一部分一部分地输出?
printf("----");
sleep(5);
printf("++++");
sleep(5);
printf("\n");
代码执行结果是:所有字符串等到执行printf("\n")时才一起输出,这也就是说printf("----")只是将字符串放到缓冲区中,在执行printf("\n")或是程序结束前并没有输出出来。有了这个概念之后,下面的问题三就比较好理解了
问题三:下面代码的输出是什么?
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
int i;
for(i=0; i<2; i++){
printf("-");
printf("ppid=%d, pid=%d, i=%d \n", getppid(), getpid(), i);
fork();
}
return 0;
}
这段代码的输出结果依旧是8个"-",解析过程如下图所示:
执行到程序结束时,系统中有图示的最下面的4个进程,图中相同进程我已使用相同颜色标识,虽然这4个进程在i=2时均没有执行printf("-"),但是其缓冲区内均由字符串"--",因此在进程结束的时候,系统会强制将缓冲区的内容刷新到输出设备上,所以会产生8个"-".
如果将上述代码修改为:
printf("-\n") ;
或是
printf("-") ;
fflush(stdout) ;
程序的输出是3个"-",也就是图中进程树的第一层和第二层执行的printf("-")的结果。
Reference: http://coolshell.cn/articles/7965.html
本文探讨了一个涉及fork系统调用的程序问题,分析了为何输出8个"-"而不是预期的6个,原因是printf的缓冲区在fork过程中被复制导致。还讨论了设备缓冲、标准输出的行缓冲特性和如何通过修改代码避免额外的输出。同时,介绍了如何通过进程关系图来理解多进程的执行情况,并给出不同代码修改方案对输出的影响。

578

被折叠的 条评论
为什么被折叠?



