Linux 等待进程结束 wait() 和 waitpid()详解

若子进程先于父进程结束时,父进程调用wait()函数和不调用wait()函数会产生两种不同的结果:
--> 如果父进程没有调用wait()和waitpid()函数,子进程就会进入僵死状态。
--> 如果父进程调用了wait()和waitpid()函数,就不会使子进程变为僵尸进程。
这是为什么呢?现在我们来深入学习wait()函数和waitpid()函数。
 
wait() 和 waitpid() 学习

1、首先我们先看一下它们的函数原型:
在终端输入命令:man 2 wait
就会看到它的函数原型:

 代码如下 复制代码
NAME
       wait, waitpid, waitid - wait for process to change state
SYNOPSIS
       #include <sys/types.h>
       #include <sys/wait.h>
       pid_t wait(int *status);
       pid_t waitpid(pid_t pid, int *status, int options);
       int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);

我们可以看到在2.6版本中新增叫了 waitid() 函数。
 
2、wait() 和 waitpid() 的功能:

1> wait()函数使父进程暂停执行,直到它的一个子进程结束为止,该函数的返回值是终止运行的子进程的PID,参数status所指向的变量存放子进程的退出码,即从子进程的main函数返回的值或子进程中exit()函数的参数。如果status不是一个空指针,状态信息将被写入它指向的变量。
注意:进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait 就会收集这个子进程的信息, 并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。
2> 头文件sys/wait.h中定义了进程退出状态的宏。
我们首先看下官方的解释
a.WIFEXITED(status)     returns true if the child terminated normally, that is, by calling exit(3) or _exit(2), or by returning from main() .
翻译:
WIFEXITED(status)  若子进程是正常结束时则返回一个非零值。即调用exit(3),_exit(3) 或从main()函数返回的值。
 
b. WEXITSTATUS(status)   returns the exit status of the  child.   This  consists  of  the least  significant  8 bits of the status argument that the child specified in a call to exit(3) or _exit(2) or  as  the  argument for  a return  statement  in main().  This macro should only be employed if WIFEXITED returned true.
翻译:
WEXITSTATUS(status)    如果宏WIFEXIED返回值为非零值时,它返回子进程中exit或_exit参数中的低8位。
 
c.WIFSIGNALED(status)  returns true if the child process was terminated by a signal.
翻译:
WIFSIGNALED(status)  若子进程异常终止则返回一个非零值。
 
d. WTERMSIG(status)   returns the number of the signal that caused the  child  process  to terminate.  This macro should only be employed if WIFSIGNALED returned true.
翻译:
WTERMSIG(status)      如果宏WIFSIGNALED的返回值非零,则返回使子进程异常终止的信号编号。
 
e.WIFSTOPPED(status)   returns true if the child process was stopped by delivery  of a signal;  this  is  only possible if the call was done using WUN?TRACED or when the child is being traced (see ptrace(2)).
翻译:
WIFSTOPPED(status)  若子进程由于异常暂停,则返回一个非零值。当调用WUN?TRACED或子进程被跟踪时这才时可能的。
 
f. WSTOPSIG(status)    returns the number of the signal which caused the child to stop.This macro should only be employed if WIFSTOPPED returned true.
翻译:
WSTOPSIG(status)      如果宏WIFSTOPPED返回值非零,则返回使子进程暂停的信号编号。
 
g.WIFCONTINUED(status)     (since  Linux  2.6.10)  returns  true  if  the child process wasresumed by delivery of SIGCONT.
翻译:
WIFCONTINUED(status)     (从2.6版本后)如果孩子进程通过SIGCONT恢复则返回一个非零值。
 
3>waitpid() 函数
(1).我们先来看一个waitpid()的经典例子:当我们下载了A软件的安装程序后,在安装快结束时它又启动了另外一个流氓软件安装程序B,当B也安装结束后,才告诉你所有安装都完成了。A和B分别在不同的进程中,A如何启动B并知道B安装完成了呢?可以很简单地在A中用fork启动B,然后用waitpid()来等待B的结束。
(2).waitpid()也用来等待子进程的结束,但它用于等待某个特定进程结束。参数pid指明要等待的子进程的PID,参数status的含义与wait()函数中的status相同。options参数可以用来改变waitpid的行为,若将该参数赋值为WNOHANG,则使父进程不被挂起而立即返回执行其后的代码。
(3).waitpid()函数中参数pid的取值
还是先看下官方解释:
 The value of pid can be:
       < -1   meaning  wait  for  any  child process whose process group ID is equal to the absolute value of pid.
       -1     meaning wait for any child process.
       0      meaning wait for any child process whose  process  group  ID  is equal to that of the calling process.
       > 0    meaning  wait  for  the  child  whose process ID is equal to the value  of pid.
翻译:
pid的值可以为下己中情况:
  < -1  等待其组ID等于pid绝对值的任一子进程。
  =-1  等待任一子进程
  =0 等待其组ID等于调用进程的组ID的任一进程
  > 0  等待其进程ID等于pid的子进程退出
(4).waitpid()函数的一个应用:
如果想让父进程周期性地检查某个特定的子进程是否已经退出,可以用下面的方法:
waitpid(child_pid, (int *) 0, WNOHANG);
如果子进程尚未退出,它将返回0;如果子进程已经结束,则返回child_pid。调用失败时返回-1。失败的原因包括没有该子进程,参数不合法等。
 
3、wait()和waitpid() 函数的区别
(1).在一个子进程终止前,wait()使其调用者阻塞,而waitpid()有一个选项,可使调用者不阻塞。
(2).waitpid()并不等待在其调用之后的第一个终止子进程,它有若干个选项,可以控制它所等待的进程。
(3).对于wait(),其唯一的出错是调用进程没有子进程;对于waitpid(),若指定的进程或进程组不存在,或者参数pid指定的进程不是调用进程的子进程都可能出错。
(4).waitpid()提供了wait()没有的三个功能:一是waitpid()可等待一个特定的进程;二是waitpid()提供了一个wait()的非阻塞版本(有时希望取的一个子进程的状态,但不想使父进程阻塞,waitpid() 提供了一个这样的选择:WNOHANG,它可以使调用者不阻塞);三是waitpid()支持作业控制。
(5).wait(&status) 的功能就等于 waitpid(-1, &status, 0);
 
函数实例: 有时希望取的一个子进程的状态,但不想使父进程阻塞,waitpid() 提供了一个这样的选择:WNOHANG,它可以使调用者不阻塞

 代码如下 复制代码

#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>

int main()
{
 pid_t pr, pc;

 do
 {
  pr = waitpid(pc, NULL, WNOHANG);

  if (pr == 0)
  {
   printf("No child exited\n");
   sleep(1);
  }

 }
 while (pr == 0);

 if (pr == pc)
 {
  printf("successfully get child %d\n", pr);
 }
 else
 {
  printf("some error occured\n");
 }

 return 0;
}

 

总结
无论进程是否正常终止,内核都会向其父进程发送SIGCHLD 信号,当调用wait或waitpid函数时
(a) 如果所有的子进程都在run, 可以阻塞父进程。
(b) 如果子进程终止,则wait立即返回子进程终止状态。
(c) 如果没有子进程在运行, 立即返回error。
 
4、函数实现:
函数实例1.(先看一个简单的实例,看看进程调用wait()函数后是如何执行的?)

 代码如下 复制代码

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

int main()
{
 pid_t child;
 int i;

 child = fork();

 if (child < 0)
 {
  printf("create failed!\n");
  exit(1);
 }
 else if (0 == child)
 {
  printf("this is the child process pid= %d\n", getpid());

  for (i = 0; i < 5; i++)
  {
   printf("this is the child process print %d !\n", i + 1);
  }

  printf("the child end\n");
 }
 else
 {
  printf("this is the father process, ppid=%d\n", getppid());
  printf("father wait the child end\n");
  wait(&child);
  printf("father end\n");
 }
}

函数经过编译:
1
gcc wait.c -o wait
2
./wait
函数执行结果:
this is the father process, ppid=3303
father wait the child end
this is the child process pid= 3356
this is the child process print 1 !
this is the child process print 2 !
this is the child process print 3 !
this is the child process print 4 !
this is the child process print 5 !
the child end
father end
 
说明:
从上面的程序我们可以深入的了解wait() 函数的执行过程:
当父进程调用wait()函数后被挂起等待,直到子进程结束为止。
 
函数实例2(现在我们在通过一个实例,来深入了解wait()函数的执行过程)

 代码如下 复制代码

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

int main()
{
 pid_t pid;
 char *msg;
 int i;
 int exit_code;

 printf("tiger study how to get exit code\n");
 pid = fork();

 if (pid == 0)  /* 子进程 */
 {
  msg = "child process is running";
  i = 5;
  exit_code = 37;
 }
 else if (pid > 0) /* 父进程 */
 {
  exit_code = 0;
 }
 else
 {
  perror("process creation failed\n");
  exit(1);
 }

 if (pid > 0) /* 父进程 */
 {
  int status;
  pid_t child_pid;
  child_pid = wait(&status);

  printf("child process has exited, pid=%d\n", child_pid);
  if (WIFEXITED(status))
  {
   printf("child exited with code %d\n", WEXITSTATUS(status));
  }
  else
  {
   printf("child exited abnormally\n");
  }
 }
 else /* 子进程 */
 {
  while (i-- > 0)
  {
   puts(msg);
   sleep(1);
  }
 }

}

函数进过编译后:

$ gcc wait1.c -o wait1

$ ./wait1

函数执行结果 :
tiger study how to get exit code
 child process is running
 child process is running
 child process is running
 child process is running
 child process is running
child process has exited,pid = 3816
child exited with code 0
 
说明:
父进程调用wait()函数后被挂起(我们可以再开一个终端,输入命令:ps aux,可以看到父进程的执行结果为S)直到子进程结束。子进程结束后,wait()函数返回刚刚结束运行的子进程的pid,宏WEXITSTATUS获取子进程的退出码。
 
 
【注意】
如果调用 wait() 的进程没有已终止的子进程,不过有一个或多个子进程仍在运行,那么 wait 将阻塞到现有子进程第一个终止为止。
waitpid 函数就等待哪个进程以及是否阻塞给了我们更多控制。首先,pid 参数允许我们指定想等待的进程ID,值-1表示等待第一个终止的子进程。其次,options 参数允许我们指定附加选项。最常用的选项是 WNOHANG,它告知内核在没有已终止子进程时不要阻塞。

时间: 2024-10-04 09:05:50

Linux 等待进程结束 wait() 和 waitpid()详解的相关文章

Linux统计进程的CPU利用率命令详解

1.0 概述 在Linux的/proc文件系统,可以看到自启动时候开始,所有CPU消耗的时间片:对于个进程,也可以看到进程消耗的时间片.这是一个累计值,可以"非阻塞"的输出.获得一定时间间隔的两次统计就可以计算出这段时间内的进程CPU利用率. 所以,是否存在一种简单的,非阻塞的方式获得进程的CPU利用率? 答案是:"没有".这里给出来一个有趣的比喻:"这就像有人给你一张照片,要你回答照片中车子的速度一样" 1.1 /proc/stat 统计总CP

【Linux】定时任务crontab和at命令详解

[Linux]定时任务crontab和at命令详解 linux 系统则是由 cron (crond) 这个系统服务来控制的.Linux 系统上面原本就有非常多的计划性工作,因此这个系统服务是默认启动的.另 外, 由于使用者自己也可以设置计划任务,所以, Linux 系统也提供了使用者控制计划任务的命令 :crontab 命令. 一.crond简介 crond 是linux下用来周期性的执行某种任务或等待处理某些事件的一个守护进程,与windows下的计划任务类似,当安装完成操作系统后,默认会安装

Linux bash Shell中的变量类型详解

  这篇文章主要介绍了Linux bash Shell中的变量类型详解,变量类型共分为本地变量.局部变量.环境变量.位置变量和特殊变量等,需要的朋友可以参考下 在Linux系统中进行日常运维或者是编写脚本时,变量是再熟悉不过的了,但这些变量都有哪些类型,具体的用法又有哪些差异呢?本文整理分享给大家: 一.bash变量类型: 本地变量 局部变量 环境变量 位置变量 特殊变量(内置) 二.本地变量: varname=value:作用域为整个bash进程可以使用; 变量命名规范: 1. 只能含字母.数

代码-linux应用程序的运行流程 求详解

问题描述 linux应用程序的运行流程 求详解 事情是这样的,老师给了个作业(如下),因为课程时间等原因,开始想要啃下2.6源码的,在各种深度了解,剖析 巴拉巴拉等的linux电子书里并没有看出多大的头绪,讲到看源码更是头大,加之时间有限,所以并没有时间去仔细的研读,想要请各位大神给些指导,毕竟看到操作系统的庞大代码就不知道从哪里入手了. 如果有耐心+时间的话 更希望能够给出一些具体的解题入手步骤,如:怎么去跟踪程序的函数运行,函数间调用大概关系是怎么研究... 请不要嫌弃...知识掌握有限,想

Linux系统下mysqlcheck修复数据库命令(详解)_Mysql

mysqlcheck客户端工具可以检查和修复MyISAM表,还可以优化和分析表. 实际上,它集成了mysql工具中check.repair.analyze.optimize的功能. 有3种方式来调用mysqlcheck: shell> mysqlcheck[options] db_name [tables] shell> mysqlcheck[options] ---database DB1 [DB2 DB3...] shell> mysqlcheck[options] --all--d

一天一个shell命令 linux文本操作系列-tac,rev命令详解_linux shell

说明: tac和rev没有必然联系,唯一相同的是,他们是"反派".tac其实是cat的反写,cat正常输出,那tac就倒着输出,不过从行的最后一行输出,那rev呢?rev是每行的最后一个字母到第一个字母倒序输出. 简单实例 tac命令 文件过滤分割与合并 tac命令用于将文件已行为单位的反序输出,即第一行最后显示,最后一行先显示. 语法 tac(选项)(参数) 选项 -a或--append:将内容追加到文件的末尾: -i或--ignore-interrupts:忽略中断信号. 参数 文

linux中php-fpm进程数优化与分析详解

在几个不是很繁忙的线上服务器发现php-fpm进程数达到500多,且存在一些运行时间长达几个月的进程. 进行了以下排查,以确定:phpfpm运行是否有问题,是否需要重启. 查看这些进程是否被正常启动 由于php-fpm.log中,会以notice级别打印worker进程的启动和回收时间,故可以通过以下语句检查有哪些php没有被记录到(ps axuf可以查看到进程的父子关系):  代码如下 复制代码 $cd /path/to/php-fpm.log $for word in `ps axu | g

linux命令大全之crontab命令使用详解

  at 命令是针对仅运行一次的任务,循环运行的例行性计划任务,linux系统则是由 cron (crond) 这个系统服务来控制的.Linux 系统上面原本就有非常多的计划性工作,因此这个系统服务是默认启动的.另外, 由于使用者自己也可以设置计划任务,所以, Linux 系统也提供了使用者控制计划任务的命令 :crontab 命令. 一.crond简介crond是linux下用来周期性的执行某种任务或等待处理某些事件的一个守护进程,与windows下的计划任务类似,当安装完成操作系统后,默认会

linux中crontab命令启动/重启/停止详解

linux 系统则是由 cron (crond) 这个系统服务来控制的.Linux 系统上面原本就有非常多的计划性工作,因此这个系统服务是默认启动的.另 外, 由于使用者自己也可以设置计划任务,所以, Linux 系统也提供了使用者控制计划任务的命令 :crontab 命令. 一.crond简介 crond 是linux下用来周期性的执行某种任务或等待处理某些事件的一个守护进程,与windows下的计划任务类似,当安装完成操作系统后,默认会安装此服务 工具,并且会自动启动crond进程,cron