linux下fork机制的学习
来源:互联网 发布:java rpc 实现 编辑:程序博客网 时间:2024/05/16 01:48
欢迎关注本人的weibo http://weibo.com/718667835 ,尊重原创性,转载请注明。
一、fork进程
进程可以看做程序的一次执行过程。在linux下,每个进程有唯一的PID标识进程。PID是一个从1到32768的正整数,其中1一般是特殊进程init,其它进程从2开始依次编号。当用完32768后,从2重新开始。
首先fork进程有以下的特性:
1.fork一次调用,二次返回, 返回为0的pid_t 是子进程,如果返回大于0,则是父进程(返回值是子进程的pid)
2.fork调用使用了“写时复制”技术来创建子进程,整个父进程空间会原模原样地复制到子进程中,包括指令,变量值,程序调用栈,环境变量,缓冲区,等等。
二、面试题解
题1:
给出如下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”,写出其他进程的输出结果(不考虑进程执行顺序)。
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为根的进程树:
题目2:
请问下面的程序一共输出多少个“-”?
#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个“-”,但是,实际上这个程序会很tricky地输出8个“-”。
所以,上面的那个程序为什么会输入8个“-”,这是因为printf(“-”);语句有buffer,所以,对于上述程序,printf(“-”);把“-”放到了缓存中,并没有真正的输出(参看《C语言的迷题》中的第一题),在fork的时候,缓存被复制到了子进程空间,所以,就多了两个,就成了8个,而不是6个。
另外,多说一下,我们知道,Unix下的设备有“块设备”和“字符设备”的概念,所谓块设备,就是以一块一块的数据存取的设备,字符设备是一次存取一个字符的设备。磁盘、内存都是块设备,字符设备如键盘和串口。块设备一般都有缓存,而字符设备一般都没有缓存。
对于上面的问题,我们如果修改一下上面的printf的那条语句为:
printf
(
"-\n"
);
或是
printf
(
"-"
);
fflush
(stdout);
就没有问题了(就是6个“-”了),因为程序遇到“\n”,或是EOF,或是缓中区满,或是文件描述符关闭,或是主动flush,或是程序退出,就会把数据刷出缓冲区。需要注意的是,标准输出是行缓冲,所以遇到“\n”的时候会刷出缓冲区,但对于磁盘这个块设备来说,“\n”并不会引起缓冲区刷出的动作,那是全缓冲,你可以使用setvbuf来设置缓冲区大小,或是用fflush刷缓存。
我估计有些朋友可能对于fork()还不是很了解,那么我们把上面的程序改成下面这样:
面对这样的图你可能还是看不懂,没事,我好事做到底,画个图给你看看:
注意:上图中的我用了几个色彩,相同颜色的是同一个进程。于是,我们的pstree的图示就可以成为下面这个样子:(下图中的颜色与上图对应)
这样,对于printf(“-”);这个语句,我们就可以很清楚的知道,哪个子进程复制了父进程标准输出缓中区里的的内容,而导致了多次输出了。(如下图所示,就是我阴影并双边框了那两个子进程)
- linux下fork机制的学习
- Linux 下的 fork()
- Linux 下的 fork
- linux下的fork
- Linux 下的 fork()
- linux下fork的使用
- linux下fork的运行机制
- linux下的fork()函数
- linux下fork的运行机制
- LINUX下的fork例子
- Linux下的 fork 函数
- linux 下 fork 的 使用
- Linux下的fork()函数
- Linux下的fork 与 vfork
- 对于Linux下fork调用的理解
- linux下fork的运行机制详细解析
- linux下fork的使用一
- LINUX下FORK的运行机制详细解析
- Java中的抽象类
- 《安卓应用开发学习》——关于ImageButton图片背景的设置问题
- mysql存储过程和job
- Java虚拟机是什么
- 【翻译练习】JNI Tips.
- linux下fork机制的学习
- 编程年龄和编程能力
- java比较自定义对象相等的方法
- IIS服务器如何启用日志功能
- 杜洋工作室网址
- C#产生随机数的类
- [python] list和tuple的异同
- android 4.0 安装adobe flash player
- Vista/Win7中绕过UAC自启动