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進程領養