薄雾浓云愁永昼————僵尸进程

一、进程

进程可以看做程序的一次执行过程。在linux下,每个进程都会被分配一个唯一的数字编号,我们称之为进程标识符或PID。PID是一个从1到32768的正整数,其中1一般是特殊进程init,其它进程从2开始依次编号。当用完32768后,从2重新开始。

在一台单处理器的计算机上,同一时间只有一个进程可以运行,其他程序处于就绪状态,该状态表示程序只要得到CPU就可以运行了。每个进程运行的时间是有限的,我们称作时间片,时间片是相当短暂的,这就给我们一种程序在同时运行的错觉。Linux内核用进程调度器来决定下一个时间片分配给那个进程,它的判断依据是根据进程的优先级。高优先级的进程运行得更频繁,但长期不间断运行的进程,优先级会变低,同时,低优先级的进程随着时间的推移,优先级会不断提升,直到其能够被执行。

 

二、父进程与子进程

子进程指的是由另一进程(对应称之为父进程)所创建的进程。一个进程可能下属多个子进程,但最多只能有1个父进程,而若某一进程没有父进程,则可知该进程很可能由内核直接生成。

 

三、僵尸进程

子进程终止时,其与父进程的关联还会保持,直到父进程也正常停止或调用wait函数,因此,进程表中子进程的表项不会立刻释放,虽然子进程已不再运行,但它仍然存在于系统中,因为他的退出码还需要保存起来此时他将成为一个死进程(defunct)或僵尸进程(zombie)。如果大量的产生僵尸进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。

      若此时父进程异常终止,就由PID为1的init进程“收养”为其子进程,但因子进程现在是一个僵尸进程,所以将一直保留在进程表中,直到被init发现并释放。进程表越大,这一过程越慢。所以应该尽量避免僵尸进程,因为在init清理他们之前,他们将一直消耗系统的资源。
薄雾浓云愁永昼————僵尸进程

上面的这道程序会使父函数阻塞10秒,此时子进程早已执行完,这时该子进程就会变成僵尸进程。
薄雾浓云愁永昼————僵尸进程

由上图我们可以看到,光标还在不停闪烁,表示在子进程运行完输出 B 后,父进程还在运行,于是子进程后面有了一个   defunct 标志,这就说明它现在是一个僵尸进程。

当然,当父进程正常结束后,僵尸进程就不存在了。

薄雾浓云愁永昼————僵尸进程

四、僵尸进程的应对方法

1、wait()函数

wait()函数一般用在父进程中等待回收子进程的资源,而防止僵尸进程的产生。

 

#include <sys/types.h>

#include <sys/wait.h>

pid_twait (int * status);

 

调用 wait 函数时,调用进程将会出现下面的情况:

· 如果其所有子进程都还在运行,则阻塞。

· 若某一子进程已经终止,则获取该子进程的终止状态,将其彻底销毁后立即返回。

· 如果没有任何子进程,则立即出错返回。

 

参数 status 是一个整形指针。如果status不是一个空指针(NULL),则子进程的终止状态将存储在该指针所指向的内存单元中。如果不关心终止状态,只是想将其销毁,可以将status参数设置为NULL。

如:pid = wait(NULL)

返回值:如果执行成功则返回被销毁的子进程PID,如果有错误发生(即无子进程)则返回-1

 

注意:只要有一个子进程终止,wait就会返回。对于两个或多个子进程的父进程需要调用wait两次或多次。因为wait的返回值是终止进程的进程PID,所以父进程总能知道哪一个子进程终止了。
薄雾浓云愁永昼————僵尸进程

如上面的程序,这时候即使子进程结束了,父进程还在运行,是不会出现僵尸进程的,因为当子进程结束后,wait函数将其回收了。
薄雾浓云愁永昼————僵尸进程

wait函数的返回值是被回收的子进程的PID


薄雾浓云愁永昼————僵尸进程

注意:wait的参数一定要写,即使不需要也要把NULL写进去,否则返回值会已一直是 -1,因为出错了。

 

2、waitpid()函数

waitpid函数,会暂时停止目前进程的执行,直到有信号来到或子进程结束,所以可以用来等待某个特定进程的结束。

 

#include<sys/types.h>

#include<sys/wait.h>

pid_t waitpid(pid_t pid, int *stat_loc, int options);

 

参数PID

指定需要等待的子进程PID,若它的值为-1则返回任一子进程的信息,于是在这一功能方面waitpidwait等效。

参数 stat_loc

wait一样如果stat_loc不是空指针,waitpid将把状态信息写到它所指向的位置。

参数 options

可用来改变waitpid的行为,其中最有用的一个选项为:WNOHANG,其作用是防止waitpid将调用者的执行挂起,若pid指定的子进程没有结束,不予以等待,立即返回,返回值为0,;若结束,则返回该子进程的PID。

在父进程不打算阻塞等待子进程返回时,可以使用这个参数,父进程可使用循环定期查询子进程的状态。

若waitpid()失败,它将返回 -1。失败原因有:没有子进程、调用被某个信号中断、选项参数无效。

如果我们不想使用这个参数,也可以把options设为0。

如:waitpid(-1, &status, 0);

 

 

wait与waitpid区别:

1.    在一个子进程终止前, wait使其调用者阻塞,而waitpid有一选择项,可使调用者不阻塞。

2.    waitpid并不等待第一个终止的子进程—它有若干个选择项,可以控制它所等待的特定进程。

3.    实际上wait函数是waitpid函数的一个特例。waitpid(-1,&status, 0);