linux进程和waitpid

进程在自己的生命周期中,会处于不同的状态,其所处的状态会随着一些触发条件发生转变。使用pstop等命令查看进程信息时,可以看到关于进程状态的相关信息,通常是在名为S的一列中使用进程状态码(PROCESS STATE CODES)表示。

在ps的文档中,PROCESS STATE CODES相关的描述如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
PROCESS STATE CODES
Here are the different values that the s, stat and state output
specifiers (header "STAT" or "S") will display to describe the state of
a process:

D uninterruptible sleep (usually IO)
R running or runnable (on run queue)
S interruptible sleep (waiting for an event to complete)
T stopped by job control signal
t stopped by debugger during the tracing
W paging (not valid since the 2.6.xx kernel)
X dead (should never be seen)
Z defunct ("zombie") process, terminated but not reaped by
its parent

For BSD formats and when the stat keyword is used, additional
characters may be displayed:

< high-priority (not nice to other users)
N low-priority (nice to other users)
L has pages locked into memory (for real-time and custom IO)
s is a session leader
l is multi-threaded (using CLONE_THREAD, like NPTL pthreads
do)
+ is in the foreground process group

我们比较关注的几个状态是:D、R、S、T、Z,它们的转移关系如下:

回收子进程

当一个进程由于某种原因终止时,内核并不是立即把它从系统中清除。相反,进程被保持在一种已终止的状态(Z, zombie)中,直到进程被其父进程回收。当父进程回收已经终止的子进程时,内核将子进程的退出状态传递给父进程,然后抛弃已经终止的进程,从此时起,该进程就不存在了。

如果一个父进程在回收子进程之前就终止了,子进程将会挂到init进程下,被init进程领养,由init进程进行回收。init进程的pid为1,在系统初始化时由内核创建。

一个进程可以通过调用waitpid函数来等待其子进程终止或者停止。wait函数是waitpid的简单版,wait(&status)等价于调用waitpid(-1, &status, 0).

waitpid:

1
2
3
4
#include <sys/types.h>
#include <sys/wait.h>

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

waitpid会挂起调用它的进程直到由pid参数指定的等待集合中的一个子进程已更改状态。默认情况下,waitpid()只等待已终止的子进程,但是这个行为可以通过options参数修改。

waitpid的返回,一共有3种情况:

  1. 当正常返回的时候,waitpid返回收集到的子进程的进程ID;
  2. 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
  3. 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;

参数:

pid的值可以是:

<-1  表示等待进程组ID等于pid的绝对值的任意子进程;

-1   表示等待任意子进程;

0    表示等待其进程组ID为调用进程的pid的任意子进程;

> 0  表示等待进程ID为pid的子进程;

int *status:

如果参数status的值不是NULL,wait就会把子进程退出时的状态取出并存入其中,这是一个整数值(int),指出了子进程是正常退出还是被非正常结束的(一个进程也可以被其他进程用信号结束,我们将在以后的文章中介绍),以及正常结束时的返回值,或被哪一个信号结束的等信息。由于这些信息被存放在一个整数的不同二进制位中,所以用常规的方法读取会非常麻烦,人们就设计了一套专门的宏(macro)来完成这项工作:

WIFEXITED(status)    如果子进程正常结束,它就返回真;否则返回假。
WEXITSTATUS(status)    如果WIFEXITED(status)为真,则可以用该宏取得子进程exit()返回的结束代码。
WIFSIGNALED(status)    如果子进程因为一个未捕获的信号而终止,它就返回真;否则返回假。
WTERMSIG(status)    如果WIFSIGNALED(status)为真,则可以用该宏获得导致子进程终止的信号代码。
WIFSTOPPED(status)    如果当前子进程被暂停了,则返回真;否则返回假。
WSTOPSIG(status)    如果WIFSTOPPED(status)为真,则可以使用该宏获得导致子进程暂停的信号代码。

option:

option的值可以用来指定waitpid的行为,主要是指定在什么情况下予以返回。option的值是0或者以下参数的或值:

WNOHANG: 当没有子进程退出时,立即返回,返回值为0。默认的行为是挂起调用进程,直到有子进程终止。在等待子进程终止的同时,如果想执行一些其他的工作,可以使用此项。

WUNTRANCED:返回已终止(terminated, Z)和被停止(stopped, T)的进程。默认只返回已终止的(terminated)进程。

WCONTINUED (since Linux 2.6.10) : 当stopped 的子进程被SIGCONT信号唤醒时,也返回。

option组合:

WNOHANG|WUNTRANCED 表示 立即返回,如果等待集合中的子进程没有子进程被终止(terminated, Z)或者被停止(stopped, T),返回0,否则返回相应的pid。