fork是由Linux内核提供的用于创建子进程的函数。理解其关键在于是一次调用两次返回,但两次返回是在不同的地址空间。
0 返回值
对于父进程,其返回的是子进程的ID,而子进程返回的是0,(注意并不是父进程的ID)。原因在于,每个进程控制块数据结构中记录了两个ID,自身ID和父进程ID,显然,对于子进程而言,没有必要知道父进程ID,因为在fork后就记录在进程控制块中。但相反,对于父进程而言,是无法知晓的,因此有必要知道子进程ID。
1 孤儿进程
简单说就是当父进程退出后,子进程仍然运行。那么该子进程就称为孤儿进程,体现在“孤儿子进程getppid()返回1”,即当父进程退出后,子进程托付给1号进程即init进程管理,其父进程变为1进程。
2 僵尸进程
与孤儿进程对应,即子进程退出,而父进程没有退出。那么子进程就处于僵尸状态,defunt。根本原因在于,子进程退出后需要等待父进程查询,且回收申请的资源,如不回收则一直处于僵尸状态,一直占有着资源,不释放,造成资源的浪费(最明显的就是有限的PID号)。
解决办法:利用信号。子进程退出时,会向父进程发SIGCHILD信号,只要父进程处理该信号,即可完成资源回收的工作。
|
需要说明的是回收资源的语句是用while((pid=waitpid(-1,&stat,WNOHANG))>0),理解这句话很关键。
3 waitpid
首先,解释为什么使用while循环。假设父进程创建了5个子进程,且基本在同一时间退出,向父进程发送SIGCHLD信号,由于信号是非实时的,因此,系统只会保留一个。父进程进入处理函数,并且回收,此时如果没有while就直接退出了。
其次,-1是表示等待任意一个进程,无论是不是该进程组。而如果该参数大于0为pid,那么表示只等待ID号为pid的子进程,如果参数等于0,表示等待同一进程组的任意进程。如果小于0,那么表示等待一个指定进程组的任何子进程,这个进程组的ID等于PID的绝对值。
然后,WNOHANG的意义在于,不阻塞式回收。此时的返回规则是:如果有子进程,但是没有结束,那么返回0,相反如果有僵尸进程,那么就会返回子进程PID。如果没有WNOHANG选项,则会一直阻塞。试想,如果只退出一个子进程,那么接收完该信号后,回到while循环,此时若没有WNOHANG那么一直阻塞。
最后,使用waitpid而不是wait的原因在于,wait就是阻塞式等待任一进程退出,没错,与waitpid(-1,&stat,0);效果是一样的。因此,如果使用while则会一直阻塞父进程,而如果使用if +wait会导致信号丢失。(见“为什么使用while”)