linux 系统调用fork
2014-08-07 17:33
295 查看
秒杀linux下系统调用fork()面试题 为什么fork执行两次呢? http://www.itmian4.com/forum.php?mod=viewthread&tid=3248&extra=page%3D2%26filter%3Dtypeid%26typeid%3D116%26typeid%3D116 第一道题(在之前博客也写过这道题:http://blog.csdn.net/chdhust/article/details/8535915): 题目:请问下面的程序一共输出多少个“-”?
要讲清这个题,我们首先需要知道fork()系统调用的特性, fork()系统调用是Unix下以自身进程创建子进程的系统调用,一次调用,两次返回,如果返回是0,则是子进程,如果返回值>0,则是父进程(返回值是子进程的pid),这是众为周知的。 还有一个很重要的东西是,在fork()的调用处,整个父进程空间会原模原样地复制到子进程中,包括指令,变量值,程序调用栈,环境变量,缓冲区,等等。 所以,上面的那个程序为什么会输入8个“-”,这是因为printf(“-”);语句有buffer,所以,对于上述程序,printf(“-”);把“-”放到了缓存中,并没有真正的输出,在fork的时候,缓存被复制到了子进程空间,所以,就多了两个,就成了8个,而不是6个。 另外,多说一下,我们知道,Unix下的设备有“块设备”和“字符设备”的概念,所谓块设备,就是以一块一块的数据存取的设备,字符设备是一次存取一个字符的设备。磁盘、内存都是块设备,字符设备如键盘和串口。块设备一般都有缓存,而字符设备一般都没有缓存。 对于上面的问题,我们如果修改一下上面的printf的那条语句为:
我估计有些朋友可能对于fork()还不是很了解,那么我们把上面的程序改成下面这样:
注意:上图中的我用了几个色彩,相同颜色的是同一个进程。于是,我们的pstree的图示就可以成为下面这个样子:(下图中的颜色与上图对应) 这样,对于printf(“-”);这个语句,我们就可以很清楚的知道,哪个子进程复制了父进程标准输出缓中区里的的内容,而导致了多次输出了。(如下图所示,就是我阴影并双边框了那两个子进程) 第二道题: 给出如下C程序,在linux下使用gcc编译: #include "stdio.h"#include "sys/types.h"#include "unistd.h"int main(){ pid_t pid1; pid_t pid2; pid1 = fork(); pid2 = fork(); printf("pid1:%d, pid2:%d\n", pid1, pid2);} 要求如下: 已知从这个程序执行到这个程序的所有进程结束这个时间段内,没有其它新进程执行。 1、请说出执行这个程序后,将一共运行几个进程。 2、如果其中一个进程的输出结果是“pid1:1001, pid2:1002”,写出其他进程的输出结果(不考虑进程执行顺序)。 这里先列出一些必要的预备知识,对linux下进程机制比较熟悉的朋友可以略过。 1、进程可以看做程序的一次执行过程。在linux下,每个进程有唯一的PID标识进程。PID是一个从1到32768的正整数,其中1一般是特殊进程init,其它进程从2开始依次编号。当用完32768后,从2重新开始。 2、linux中有一个叫进程表的结构用来存储当前正在运行的进程。可以使用“ps aux”命令查看所有正在运行的进程。 3、进程在linux中呈树状结构,init为根节点,其它进程均有父进程,某进程的父进程就是启动这个进程的进程,这个进程叫做父进程的子进程。 4、fork的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等)数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程。 有了上面的预备知识,我们再来看看解题的关键。我认为,解题的关键就是要认识到fork将程序切成两段。看下图: 上图表示一个含有fork的程序,而fork语句可以看成将程序切为A、B两个部分。然后整个程序会如下运行: step1、设由shell直接执行程序,生成了进程P。P执行完Part. A的所有代码。 step2、当执行到pid = fork();时,P启动一个进程Q,Q是P的子进程,和P是同一个程序的进程。Q继承P的所有变量、环境变量、程序计数器的当前值。 step3、在P进程中,fork()将Q的PID返回给变量pid,并继续执行Part. B的代码。 step4、在进程Q中,将0赋给pid,并继续执行Part. B的代码。 这里有三个点非常关键: 1、P执行了所有程序,而Q只执行了Part. B,即fork()后面的程序。(这是因为Q继承了P的PC-程序计数器) 2、Q继承了fork()语句执行时当前的环境,而不是程序的初始环境。 3、P中fork()语句启动子进程Q,并将Q的PID返回,而Q中的fork()语句不启动新进程,仅将0返回。 解题 下面利用上文阐述的知识进行解题。这里我把两个问题放在一起进行分析。 1、从shell中执行此程序,启动了一个进程,我们设这个进程为P0,设其PID为XXX(解题过程不需知道其PID)。 2、当执行到pid1 = fork();时,P0启动一个子进程P1,由题目知P1的PID为1001。我们暂且不管P1。 3、P0中的fork返回1001给pid1,继续执行到pid2 = fork();,此时启动另一个新进程,设为P2,由题目知P2的PID为1002。同样暂且不管P2。 4、P0中的第二个fork返回1002给pid2,继续执行完后续程序,结束。所以,P0的结果为“pid1:1001, pid2:1002”。 5、再看P2,P2生成时,P0中pid1=1001,所以P2中pid1继承P0的1001,而作为子进程pid2=0。P2从第二个fork后开始执行,结束后输出“pid1:1001, pid2:0”。 6、接着看P1,P1中第一条fork返回0给pid1,然后接着执行后面的语句。而后面接着的语句是pid2 = fork();执行到这里,P1又产生了一个新进程,设为P3。先不管P3。 7、P1中第二条fork将P3的PID返回给pid2,由预备知识知P3的PID为1003,所以P1的pid2=1003。P1继续执行后续程序,结束,输出“pid1:0, pid2:1003”。 8、P3作为P1的子进程,继承P1中pid1=0,并且第二条fork将0返回给pid2,所以P3最后输出“pid1:0, pid2:0”。 9、至此,整个执行过程完毕。 所得答案: 1、一共执行了四个进程。(P0, P1, P2, P3) 2、另外几个进程的输出分别为: pid1:1001, pid2:0 pid1:0, pid2:1003 pid1:0, pid2:0 进一步可以给出一个以P0为根的进程树: 代码验证结果: 第三道题: 请补足横线,使之输出welcome xiyoulinux if(_____) printf(“xiyoulinux ”); else printf(“welcome”); 这道题如果在if中只写fork(),也不知道打印出来什么顺序的,所以想到函数wait(),等待子进程结束。 程序改写为: 运行结果: 可见这样才得到了正确的运行结果。 第四道题: int main() { return fork()&&fork()||fork(); } 问题:1.一共产生几个进程 2.返回值为1的概率为多少?这道题一看头都晕了,但是抓其根本,再难也都能解决的!! 其实对于fork的分析,最直观的做法也最有效,画出进程关系图,两个问题便迎刃而解这里要用到的有关fork的知识并不多,只需要知道fork的返回值:fork是一次调用,两次返回;子进程的返回值为0,父进程的返回值是新产生子进程的进程号(大于0)。对于&&操作符,若前操作数为0,则直接跳过后操作数的判断。基于以上两个基本理论,通过图示,即可得出结论: 图中同一色彩的框图代表同一进程,可见,此时共产生5个进程;结合表达式的最终值分布情况可得,返回1的概率为3/5 造成此种结果,最主要的诱因,是fork()后,父进程与子进程返回没有先后顺序,有时父进程先返回,有时子进程先返回。所以,根据父子进程返回的不同结果,逻辑运算符&&和||选择是否执行其后的程序代码(此处即为fork)。 第五道题(EMC的一道笔试题) int main(int argc, char* argv[]) { fork(); fork() && fork() || fork(); fork(); } 不算main这个进程自身,到底创建了多少个进程啊? 这道题和第四道题比较类似,第一个fork()和最后一个fork()一定会执行,中间的同上图,在这里不画了。 所以这道题解法为:加上前面的fork和最后的fork,所有的进程都会执行,会产生4个分支,总共4*5=20个分支,也就是20个进程,除去main主进程,就是19个进程了。 |
blog 来源http://www.itmian4.com/forum.php?mod=viewthread&tid=3364&extra=page%3D1%26filter%3Dtypeid%26typeid%3D116%26typeid%3D116
相关文章推荐
- [转]linux系统调用fork, vfork, clone
- Linux中fork系统调用编译出错/usr/bin/ld: cannot open output file fork_test: Is a directory co
- Linux 系统调用之 fork()——进程的创建
- linux 系统调用fork()
- 一道关于Linux系统下fork系统调用的面试题
- Linux系统进程控制编程(二)——fork系统调用
- Linux系统调用fork()用法详解
- 九、Linux系统编程-进程(二)fork系统调用、复制进程映像、写时复制、孤儿进程和僵尸进程
- Linux中fork系统调用
- linux系统下 fork()系统调用: 关于父子进程缓存问题的小坑
- 一道关于Linux系统下fork系统调用的面试题
- 秒杀linux下系统调用fork()面试题
- linux系统调用fork()、vfork()、clone()讲解
- linux中fork()系统调用总结
- linux系统调用fork, vfork, clone
- 避免linux系统调用fork后产生僵死进程
- linux 系统调用fork vfork clone
- 秒杀linux下系统调用fork()面试题
- Linux 内核学习——系统调用(fork)
- linux系统调用之-fork、wait、waitpid