fork是一个很重要的函数,能否彻底理解父子进程的关系,取决于你对fork的理解的程度。
#include<unsitd.h>
pid_t fork(void)
从书上我总结了三点:
-
fork调用一次返回两次,子进程返回0,父进程返回子进程ID
-
父子进程共享正文段,拥有父进程数据段,堆和栈的副本
-
对于第二点的父进程的数据段,堆,栈,fork采用一种写时复制技术,需要的时候才复制一个副本
看一个如下的程序:
#include <apue.h>
int glob = 6;
char buf[] = "a write to stdout";
int main(int argc, char *argv[])
{
int var;
pid_t pid;
var = 88;
if(write(STDOUT_FILENO,buf,sizeof(buf)-1) != sizeof(buf)-1)
err_sys("write error");
printf("brfore fork\n");
if((pid = fork()) < 0)
err_sys("fork error\n");
else if(pid == 0)
{
glob++;
var++;
}
else
sleep(2);
printf("pid=%d,ppid=%d,glob=%d,var=%d\n",getpid(),getppid(), glob, var);
exit(0);
}
vfork函数:
#include <sys/types.h>
#include <unistd.h>
pid_t vfork(void);
vfork:createa child process and block parent
1.vfork的目的是exec一个新程
2.vfork保证子进程先运行,在它调用exec或exit之后父进程才可能被调度运行
看一个程序:
#include <apue.h>
int glob = 6;
int main(void)
{
int var = 88;
pid_t pid;
printf("before vfork\n");
if((pid = vfork())<0)
err_sys("vfork error");
else if(pid == 0)
{
glob++;
var++;
printf("before parent!\n");
_exit(0);
}
/*parent continue here*/
printf("pid=%d,ppid=%d,glob=%d,var=%d\n",getpid(),getppid(),glob,var);
exit(0);
}
接下来,我们在理解另外两个概念:
init进程:
进程ID为1,在系统自举过程结束时由内核调用,该进程不会终止
如果一个进程的父进程已经终止,那么该进程会被init进程领养
僵尸进程:
一个已经终止,但是其父进程尚未对其处理的进程
看一个程序:
#include <apue.h>
#include <sys/wait.h>
int main(void)
{
pid_t pid;
if((pid=fork()) < 0)
err_sys("fork error\n");
else if(pid == 0)
{
if((pid=fork()) < 0)
err_sys("fork error");
else if(pid > 0)
{
printf("first child pid=%d ppid=%d\n",getpid(),getppid());
exit(0);
}
sleep(2);
printf("seccond child,parent pid=%d\n",getppid());
printf("second child pid=%d\n",getpid());
exit(0);
}
if(waitpid(pid,NULL,0) != pid)
err_sys("waitpid error\n");
printf("parent pid=%d ppid=%d\n",getpid(),getppid());
exit(0);
}
这个程序融合了上面的两个概念:
-
这个程序调用了fork两次,且第二次使第一个子进程终止了,父进程调用了waitpid(由于受到子进程终止时,内核发送的SIGCHILD信号而调用),所以第一进程不会成为僵尸进程
-
第二个子进程在它的父进程终止后会被init进程领养