一、fork函数的定义与特点
定义:在进程控制中,父进程通过调用fork函数来创建子进程。
特点:
- 新创建的子进程几乎但不完全与父进程相同。子进程得到与父进程用户级虚拟空间相同的(但是独立的)一本副本,包括代码和数据段、堆、共享库以及用户栈。子进程还获得与父进程任何人打开文件描述符相同的副本,这就意味着当父进程调用fork时,子进程可以读写父进程中打开的任何文件;
- 调用一次fork函数后当前时间节点下每个进程都会产生一个与父进程状态完全相同的子进程,假如当前只有一个进程,则调用一次fork会返回两次,如接下来的例子1;
- 父进程和子进程是并发运行的独立进程。简单来说就是,你也不知道父进程和子进程哪个先执行完,可能在这个系统中,是父进程先执行完,但是在另一个系统中就是子进程先执行完。因此,作为程序员,我们绝不能对不同进程中指令的交替执行做任何假设;
- 相同但是独立的地址空间。子进程和父进程拥有完全相同的运行状态(除了pid不同,啥都形同,这也是区分子进程和父进程的方法,在父进程中,fork返回子进程的pid,在子进程中,fork返回0),但是两个进程在不同的地址空间中运行,也就是说,修改一个进程中的数据,并不会对另一个进程造成影响;
- 共享文件。在接下来的例子1中,我们可以发现子进程和父进程都把它们的输出显示在屏幕上。原因是子进程继承了父进程中所有的打开文件。当父进程调用fork时,stdout文件是打开的,并指向屏幕。子进程继承了这个文件,因此它的输出也是指向屏幕的。
例子1(主要代码如下)
int main(int argc, char *argv[])
{
pid_t pid;
int x = 1;
pid = fork(); //line:ecf:forkreturn
if (pid == 0) { /* Child */
printf("child : x=%d\n", ++x); //line:ecf:childprint
fflush(stdout);
return 0;
}
/* Parent */
printf("parent: x=%d\n", --x); //line:ecf:parentprint
fflush(stdout);
return 0;
}
在Linux中运行结果如下
二、用进程图来理清fork的思路
进程图的画法:每个顶点a对应于一条程序语句的执行。有向边a→b表示语句发生在语句b之前。边上可以标记一些信息,例如一个变量的当前值。每张图从一个顶点开始,对应于调用main的父进程。这个顶点没有入边,并且只有一个出边。每个进程的顶点序列结束语一个对应于exit调用哦个的顶点。这个顶点只有一条蠕变,没有出边。例子1的进程图如下。
再看一个例子,代码如下:
int main(){
fork();
fork();
printf("hello\n");
exit(0);
}
运行结果及进程图如下:
本文参考文献——《深入理解计算机系统》