fork在英文中的是"分支,分叉"的意思,而在Linux系统,fork调用后也产生了分支:一个分支为父进程,另一个分支为子进程,分支的依据就是fork调用的返回值。在父进程中,fork调用返回子进程的pid,而在子进程中,返回值为0,如下所示。
pid_t pid;
pid = fork();
if (pid == 0) {
//子进程
} else {
// 父进程
}
fork调用产生的子进程复制(严格说是写时复制)了父进程的地址空间,这意味着父进程中的代码段、数据段都在子进程中,因此父进程中的代码被子进程共享,可以直接调用;而父进程中的全局变量也被子进程继承。但是全局变量在两个进程中是独立的,一个进程修改后并不会影响另一个进程(写时复制)。除此之外,父进程中的信号掩码、打开的文件描述符、共享内存、消息队列、信号量等;但父进程中待处理的信号、异步I/O操作、信号量调整值(SEM_UNDO)不会被继承。
当子进程调用execl函数族后,子进程将不再于父进程共享地址空间。这种情况常用于启动一个程序。Linux中的system函数,本质上也是fork后在调用exec函数。
当子进程退出后,父进程需要调用wait函数来回收子进程的资源,否则子进程将变成僵尸进程。如果父进程不想调用wait函数阻塞,也可以处理信号SIGCHLD。还有一种方式,就是采用所谓的孙进程,让init进程负责回收。具体的做法是,父进程先创建子进程,然后子进程再创建孙进程,并先于孙进程退出,这样孙进程就变成孤儿进程,被init程序接管。代码示意如下。
pid_t pid;
pid = fork();
if (pid == 0) {
// 子进程
pid = fork();
if (pid == 0) {
// 孙进程
sleep(1);
// execl
} else {
exit(EXIT_SUCCESS);
}
} else {
// 父进程
}