一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用wait或waitpid获取这些信息,然后彻底清除掉这个进程。我们知道一个进程的退出状态可以在Shell中用特殊变量$?查看,因为Shell是它的父进程,当它终止时Shell调用wait或waitpid得到它的退出状态同时彻底清除掉这个进程。
如果一个进程已经终止,但是它的父进程尚未调用wait或waitpid对它进行清理,这时的进程状态称为僵尸(Zombie)进程。任何进程在刚终止时都是僵尸进程,正常情况下,僵尸进程都立刻被父进程清理了.
init进程。init是系统中的一个特殊进程,通常程序文件是/sbin/init,进程id是1,在系统启动时负责启动各种系统服务,之后就负责清理子进程,只要有子进程终止,init就会调用wait函数清理它。
僵尸进程是不能用kill命令清除掉的,因为kill命令只是用来终止进程的,而僵尸进程已经终止了。
当一个进程正常或异常终止时,内核就向其父进程发送SIGCHLD信号。父进程可以忽略该信号,或调用信号处理函数。
若调用成功则返回清理掉的子进程id,若调用出错则返回-1。父进程调用wait或waitpid时可能会:
1.阻塞(如果它的所有子进程都还在运行)。
2.带子进程的终止信息立即返回(如果一个子进程已终止,正等待父进程读取其终止信息)。
3.出错立即返回(如果它没有任何子进程)。
这两个函数的区别是:
1.如果父进程的所有子进程都还在运行,调用wait将使父进程阻塞,而调用waitpid时如果在options参数中指定WNOHANG可以使父进程不阻塞而立即返回0。
2.wait等待第一个终止的子进程,而waitpid可以通过pid参数指定等待哪一个子进程。
可见,调用wait和waitpid不仅可以获得子进程的终止信息,还可以使父进程阻塞等待子进程终止,起到进程间同步的作用。如果参数status不是空指针,则子进程的终止信息通过这个参数传出,如果只是为了同步而不关心子进程的终止信息,可以将status参数指定为NULL。
子进程的终止信息在一个int中包含了多个字段,用宏定义可以取出其中的每个字段,下面有四个互斥的宏。
WIFEXITED(status)
若为正常终止子进程返回的状态,则为真。对于这种情况可以执行WEXITSTATUS(status),取子进程传送给exit、_exit或_Exit参数的低8位。
WIFSIGNALED(status)
若为异常终止子进程返回的状态,则为真(接到一个不捕捉的信号)。对于这种情况可以执行WTERMSIG(status),取得子进程终止的信号编号。
WIFSTOPPED(status)
若为当前暂停子进程返回的状态,则为真。对于这种情况可以执行WSTOPSIG(status),取得子进程暂停的信号编号。
WIFCONTINUED(status)
若在作业控制暂停后已经继续的子进程返回的状态,则为真。仅用于waitpid。
对于waitpid函数中的pid参数的作用见下表:
pid == -1 等待任一子进程。
pid > 0 等待其进程ID与pid相等的子进程
pid == 0 等待其组ID等于调用进程组ID的任一的子进程
pid < -1 等待其组ID等于pid绝对值的任一的子进程
对于waitpid函数中的options参数的作用见下表:
WCONTINUED
若实现支持作业控制,那么由pid指定的任一子进程在暂停后已经继续,但是状态没报告,则返回其状态
WNOHANG
若由pid指定的子进程并不是立即可用的,则waitpid不阻塞,此时返回值为0
WUNTRACED
若实现支持作业控制,那么由pid指定的任一子进程已经处于暂停状态并没报告过,则返回其状态