lienhua34
2014-10-12

当一个进程正常或者异常终止时,内核就向其父进程发送 SIGCHLD信号。父进程可以选择忽略该信号,或者提供一个该信号发生时即被调用的函数(信号处理程序)。对于这种信号的系统默认动作是忽略它。

在文档“进程控制三部曲”中,我们讲的第三部曲是使用 wait 函数来获取终止子进程的终止状态。那么,有几个问题我们这里需要详细的学习一下。

1. 父进程一定能够获取到子进程的终止状态吗?如果子进程在父进程调用 wait 函数前就终止了,怎么办?

2. 如果父进程没有获取子进程的终止状态,那会发生什么?

3. 如果父进程有多个子进程,那么获取的是哪个子进程的终止状态呢?

对于第一个问题的回答是:内核为每个终止进程保存了一定量的信息,包括进程 ID、该进程的终止状态、以及该进程使用的 CPU 时间总量。所以,当终止进程的父进程调用 wait 或者 waitpid 函数,即可获取到这些信息。当父进程获取终止进程的终止信息之后,内核就可以释放终止进程所使用的所有存储区、关闭其所有打开的文件。

在 UNIX 术语中,一个已经终止、但是其父进程尚未对其进行善后处理(获取终止子进程的相关信息)的进程被称为僵尸进程(zombie)。如果编写一个长期运行的程序,调用 fork 产生子进程之后,需要调用 wait 来获取这些子进程的终止状态,否则这些子进程在终止之后将会变成僵尸进程。(后面会讲到用一个技巧以避开父进程调用 wait 获取所有子进程的终止状态。)

那么如果那些被 init 进程领养的进程在终止之后会不会也变成僵尸进程?答案是:不会。因为 init 进程无论何时只要有一个子进程终止,init 就会调用 wait 函数获取其终止状态。

那么关于上面的第三个问题,我们得通过详细学习 wait 和 waitpid 函数才能都做出回答了。

#include <sys/wait.h>

pid_t wait(int *statloc);

返回值:若成功则返回终止进程的ID,若出错则返回-1

参数 statloc 是一个整形指针。如果 statloc 不是一个空指针,则终止进程的终止状态将存储在该指针所指向的内存单元中。如果不关心终止状态,可以将 statloc 参数设置为空。

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

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

• 如果一个子进程已经终止,正等待父进程获取其终止状态,则获取该子进程的终止状态然后立即返回。

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

wait 函数获取的终止状态是一个 int 型数值,那我们如何得到具体的终止信息呢?POSIX.1 规定终止状态用定义在 <sys/wait.h> 中的各个宏来参看。有四个互斥的宏可以用来得到进程的终止原因。这四个宏见表 1,

表 1: 检查终止状态的宏
说明
WIFEXITED(status) 若正常终止子进程返回的状态,则为真。此种情况,调用 WEXITSTATUS(status) 可以获取子进程调用 exit 函数的参数的低 8位。
WIFSIGNALED(status) 若为异常终止子进程返回的状态,则为真。此种情况,调用 WTERMSIG(status) 取得使子进程终止的信号编号。
WIFSTOPPED(status) 若为当前暂停子进程返回的状态,则为真。
WIFCONTINUED(status) 若在作业控制暂停后已经继续的子进程返回了状态,则为真。

下面我们来看一下打印终止进程状态说明的例子,

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>

extern void print_exit(int status);

int
main(void)
{
  pid_t pid;
  int status;

  if ((pid = fork()) < 0) {
    printf("fork error: %s\n", strerror(errno));
    exit(-1);
  } else if (pid == 0) {
    exit(8);
  }
  if (wait(&status) != pid) {
    printf("wait error: %s\n", strerror(errno));
    exit(-1);
  }
  print_exit(status);

  if ((pid = fork()) < 0) {
    printf("fork error: %s\n", strerror(errno));
    exit(-1);
  } else if (pid == 0) {
    abort();
  }
  if (wait(&status) != pid) {
    printf("wait error: %s\n", strerror(errno));
    exit(-1);
  }
  print_exit(status);
  
  exit(0);
}

void print_exit(int status)
{
  if (WIFEXITED(status)) {
    printf("normal termination, exit status = %d\n",
           WEXITSTATUS(status));
  } else if (WIFSIGNALED(status)) {
    printf("abnormal termination, signal number =%d\n",
           WTERMSIG(status));
  }
}
waitdemo.c

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-08-09
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-01-17
猜你喜欢
  • 2022-12-23
  • 2021-06-22
  • 2021-09-19
  • 2022-12-23
  • 2021-07-10
  • 2021-06-27
相关资源
相似解决方案