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进程领养