转载--wait waitpid解释

作者:lxmuyu

链接:wait、waitpid

wait 
1.1 简介

wait函数所需头文件: 
#include <sys/types.h>
#include <sys/wait.h>
wait函数原型:
pid_t wait(int *status);

进程一旦调用了 wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子 进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。 
参数status用来保存 被收集进程退出时的一些状态,它是一个指向int类型的指针。但如果我们对这个子进程是如何死掉的毫不在意,只想把这个僵尸进程消灭掉,(事实上绝大多数 情况下,我们都会这样想),我们就可以设定这个参数为NULL,就象下面这样: 
pid = wait(NULL);

如果成 功,wait会返回被收集的子进程的进程ID,如果调用进程没有子进程,调用就会失败,此时wait返回-1,同时errno被置为ECHILD。 
1.2 实战 
下面就让我们用一个例子来实战应用一下wait调用,程序中用到了系统调用fork,如果你对此不大熟悉或已经忘记了,请参考fork函数的使用。 
/* wait1.c */
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
pid_t pc,pr;
pc = fork();
if (pc < 0)                                                                         /* 如果出错 */
printf("error ocurred!\n");
else if (pc == 0)                                                               /* 如果是子进程 */

printf("This is child process with pid of %d\n",getpid());
sleep(10);                                                                /* 睡眠10秒钟 */
}
else                                                                                 /* 如果是父进程 */
{                                                                              
pr = wait(NULL);                                                      /* 在这里等待 */
printf("I catched a child process with pid of %d\n"),pr); 
exit(0);

}

编译并运行: 
# cc wait1.c -o wait1
# ./wait1
#This is child process with pid of 1508I 
#catched a child process with pid of 1508

可以明显注意到,在第2行结果打印出来前有10秒钟的等待时间,这就是我们设定的让子进程睡眠的时间,只有子进程从睡眠中苏醒过来,它才能正常退出,也就才能被父进程捕捉到。其实这里我们不管设定子进程睡眠的时间有多长,父进程都会一直等待下去,读者如果有兴趣的话,可以试着自己修改一下这个数值,看看会出现怎样的结果。 
1.3 参数status 
如果参数status的值不是NULL,wait就会把子进程退出时的状态取出并存入其中,这是一个整数值(int),指出了子进程是正常退出 还是被非正常结束的(一个进程也可以被其他进程用信号结束),以及正常结束时的返回值,或被哪一个信号结束的等信息。由于这些信息被存放在一个整数的不同二进制位中,所以用常规的方法读取会非常麻烦,人们就设计了一套专门的宏(macro)来完成这项工作,下面我们来学习一下其 中最常用的两个: 
1,WIFEXITED(status) 这个宏用来指出子进程是否为正常退出的,如果是,它会返回一个非零值。 (请注意,虽然名字一样,这里的参数status并不同于wait唯一的参数--指向整数的指针status,而是那个指针所指向的整数,切记不要搞混了。) 
2,WEXITSTATUS(status) 当WIFEXITED返回非零值时,我们可以用这个宏来提取子进程的返回值,如果子进程调用exit(5)退出,WEXITSTATUS(status) 就会返回5;如果子进程调用exit(7),WEXITSTATUS(status)就会返回7。请注意,如果进程不是正常退出的,也就是说,WIFEXITED返回0,这个值就毫无意义。 
下面通过例子来实战一下我们刚刚学到的内容: 
/* wait2.c */
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{
int status;
pid_t pc,pr;
pc = fork();                                                                             /*调用fork函数*/
if (pc < 0)                                                                               /* 如果出错 */
printf("error ocurred!\n");
else if (pc == 0)                                                                      /* 子进程 */
{
printf("This is child process with pid of %d.\n",getpid());
exit(3);                                                                             /* 子进程返回3 */
}
else                                                                                         /* 父进程 */
{
pr = wait(&status);
if (WIFEXITED(status))                                                                    
{
printf("the child process %d exit normally.\n",pr);
printf("the return code is %d.\n",WEXITSTATUS(status));
}
else                                                                                  /* 如果WIFEXITED返回零 */
printf("the child process %d exit abnormally.\n",pr);
}
}

编译并运行: 
# cc wait2.c -o wait2
# ./wait2
#This is child process with pid of 1538.
#the child process 1538 exit normally.
#the return code is 3. 
#the child process 1538 exit abnormally.

父进程准确捕捉到了子进程的返回值3,并把它打印了出来。 
当然,处理进程退出状态的宏并不止这两个,但它们当中的绝大部分在平时的编程中很少用到,就也不在这里浪费篇幅介绍了,有兴趣的读者可 以自己参阅Linux man pages去了解它们的用法。 
waitpid 
2.1 简介 
waitpid系统调用在Linux函数库中的所需头文件:
#include <sys/types.h>
#include <sys/wait.h>
waitpid系统调用在Linux函数库中的原型是: 
pid_t waitpid(pid_t pid,int *status,int options);
从本质上讲,系统调用waitpid和 wait的作用是完全相同的,但waitpid多出了两个可由用户控制的参数pid和options,从而为我们编程提供了另一种更灵活的方式。下面我们 就来详细介绍一下这两个参数: 
pid 
从参数的名字pid和类型 pid_t中就可以看出,这里需要的是一个进程ID。但当pid取不同的值时,在这里有不同的意义。 
pid>0时,等待进程ID等于 pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。 
pid=-1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。 
pid=0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。 
pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。 
options
 
options提供了一些额外的选项来控制waitpid,目前在Linux中只支持WNOHANGWUNTRACED两个选项,这是两个常数,可以用"|"运算符把它们连接起来使用,比如: 
ret=waitpid(-1,NULL,WNOHANG | WUNTRACED);

如果我们不想使用它们,也可以把options设为0,如: 
ret=waitpid(-1,NULL,0);

如果使用了 WNOHANG参数调用waitpid,如果没有任何已终止的进程,它也会立即返回,不会像wait那样永远等下去。 
而WUNTRACED参数,如果子进程进入暂停执行则马上返回,但终止状态不予理睬。
看到这里,聪明的读者可能已经看出端倪了--wait不就是经过包装的waitpid吗?没错,察看<内核源码目录>/include/unistd.h文件349-352行就会发现以下程序段: 
static inline pid_t wait(int * wait_stat){return waitpid(-1,wait_stat,0);}

2.2 返回值和错误 
waitpid的返回值比wait稍微复杂一些,一共有3种情况: 
当正常返回的时候,waitpid返回收集到的子进程的进程ID; 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0; 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在; 当pid所指示的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid就会出错返回,这时errno被设置为ECHILD; 
/* waitpid.c */
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{
pid_t pc, pr;
pc = fork();
if (pc < 0)                                                                    /* 如果fork出错 */
printf("Error occured on forking.\n");
else if (pc == 0)                                                          /* 如果是子进程 */
{
sleep(10);                                                                 /* 睡眠10秒 */
exit(0);
}
else                                                                                /* 如果是父进程 */
do
{
pr = waitpid(pc, NULL, WNOHANG);                      /* 使用了WNOHANG参数,waitpid不会在这里等待 */
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");

编译并运行: 
#gcc waitpid.c -o waitpid
#./waitpid
#No child exited
#No child exited
#No child exited
#No child exited
#No child exited
#No child exited
#No child exited
#No child exited
#No child exited
#No child exited
#successfully get child 1526 
父进程经过10次失败的尝试之 后,终于收集到了退出的子进程。 
因为这只是一个例子程序,不便写得太复杂,所以我们就让父进程和子进程分别睡眠了10秒钟和1秒钟,代表它们分 别作了10秒钟和1秒钟的工作。父子进程都有工作要做,父进程利用工作的简短间歇察看子进程的是否退出,如退出就收集它。

时间: 2024-08-27 07:25:03

转载--wait waitpid解释的相关文章

恶意的转载:名词解释【蝴蝶效应】【青蛙效应】......

[蝴蝶效应] 蝴蝶效应:上个世纪70年代,美国一个名叫洛伦兹的气象学家在解释空气系统理论时说,亚马逊雨林一只蝴蝶翅膀偶尔振动,也许两周后就会引起美国得克萨斯州的一场龙卷风. 蝴蝶效应是说,初始条件十分微小的变化经过不断放大,对其未来状态会造成极其巨大的差别.有些小事可以糊涂,有些小事如经系统放大,则对一个组织.一个国家来说是很重要的,就不能糊涂. 今天的企业,其命运同样受“蝴蝶效应”的影响.消费者越来越相信感觉,所以品牌消费.购物环境.服务态度……这些无形的价值都会成为他们选择的因素.所以只要稍

转载TCP-IP协议解释

本文转载自 http://www.ruanyifeng.com/blog/2009/03/tcp-ip_model.html TCP/IP模型是互联网的基础, 理解 TCP/IP对理解互联网至关重要 TCP/IP 是一系列协议的总称,这些协议的目的,是为了能够在计算机之间进行信息交换 所谓"协议"可以理解成机器之间交谈的语言,每一种协议都有自己的目的.TCP/IP模型一共包括几百种协议,对互联网上交换信息的各个方面都做了规定 TCP/IP模型有四层结构 这些协议可以大致分成四个层次,上

Docker网络解决方案-Flannel

以下是解释(转载他人): 名词解释 覆盖网络: overlay networks,运行在一个网上的网(应用层网络),并不依靠ip地址来传递消息,而是采用一种映射机制,把ip地址和identifiers做映射来资源定位. 原理 每个主机配置一个ip段和子网个数.例如,可以配置一个覆盖网络使用 10.100.0.0/16段,每个主机/24个子网.因此主机a可以接受10.100.5.0/24,主机B可以接受10.100.18.0/24的 包.flannel使用etcd来维护分配的子网到实际的ip地址之

Android编程之LayoutInflater的inflate方法实例

假设你不关心其内部实现,仅仅看怎样使用的话,直接看这篇就可以. 接上篇,接下来,就用最最简单的样例来说明一下: 用两个布局文件main 和 test: 当中,main.xml文件为: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layo

Json体验之——Json结构简介

JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate. It is based on a subset of the JavaScript Programming Language, Standard ECMA-262 3rd

Single Number II位运算解析

本题最机巧的O(n)解法最早由1337c0d3r于2013.11.26发布在leetcode.之后看到类似的,都系转载.引用或抄袭.(原文:https://oj.leetcode.com/discuss/857/constant-space-solution) 大多数转载都写得语焉不详,有的甚至据为己有.本帖旨在全面解析该算法. 如下: int singleNumber(int A[], int n) { int ones = 0, twos = 0, threes = 0; for (int

预编译命令简单解释(转载)

我的blog是用开源的BlogEngine来架设的,有的时候为了满足自己的需求及要对源代码做一些修改.在我调试客户端代码的时候,不管是使用Firebug或者是Vs 2008来调试,看到的Javascript代码都是经过动态压缩过了的,这个系统有一个HttpHanddle是专门用来处理js文件请求的,在第一次请求的时候会对js代码进行压缩,去掉了注释换行符等不必要的字符,这样可以提高访问的速度,但是对调试非常的不利,相信我们谁都不愿意对着一堆压缩过了的JS代码做调试.于是我想到了C#的预编译指令,

解释一下核主成分分析(Kernel Principal Component Analysis, KPCA)的公式推导过程(转载)

KPCA,中文名称"核主成分分析",是对PCA算法的非线性扩展,言外之意,PCA是线性的,其对于非线性数据往往显得无能为力,例如,不同人之间的人脸图像,肯定存在非线性关系,自己做的基于ORL数据集的实验,PCA能够达到的识别率只有88%,而同样是无监督学习的KPCA算法,能够轻松的达到93%左右的识别率(虽然这二者的主要目的是降维,而不是分类,但也可以用于分类),这其中很大一部分原因是,KPCA能够挖掘到数据集中蕴含的非线性信息. 今天突然心血来潮,想重新推导一下KPCA的公式,期间遇

[转载] Unix/Linux 的 Load 初级解释

原文链接:http://dbanotes.net/arch/unix_linux_load.html Unix/Linux 的 Load 初级解释 几乎每个接触类 Unix 操作系统的工程师都知道如何查看系统负载.但这东西的工作机理到底是怎样的,可能没有多少能说清楚.对比了一些相关信息,加上自己的理解,做一下笔记. 什么是 Load ? 什么是 Load Average ? Load 就是对计算机干活多少的度量(WikiPedia: the system load is a measure of