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");}
}

函数经过编译:

gcc wait.c -o wait
./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,它告知内核在没有已终止子进程时不要阻塞。

 

 

参考:

http://www.tc5u.com/linux_unix/1635564.htm

http://blog.163.com/libo_5/blog/static/15696852010324287748/

转载于:https://www.cnblogs.com/52php/p/5684575.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/403421.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

printf格式化输出类型

%d 十进制有符号整数 %u 十进制无符号整数 %f 浮点数 %s 字符串 %c 单个字符 %p 指针的值 %e 指数形式的浮点数 %x, %X 无符号以十六进制表示的整数 %0 无符号以八进制表示的整数 %g 自动选择合适的表示法 可以在”%”和字母之间加小写字母l, 表示输出的是长型数。 …

uni-app微信小程序保存页面到相册;canvas保存小程序页面;微信小程序保存二维码活动页面到相册;微信小程序canvas 生成海报保存到相册;canvas绘制小程序页面保存及分享;

文末代码可以直接复制运行&#xff08;只需要将中间的二维码图片、底部的微信和相册图片和微信头像配置白名单 改成你项目内的img图片即可成功运行&#xff09; 一、场景&#xff1a;在微信小程序 个人名片页面 含有微信头像和个人信息二维码&#xff08;识别可跳转小程序指定页…

面试经验谈架构

##################################################### #本文内容来自《老男孩linux运维实战培训》学生—郑东旭 #如有转载&#xff0c;请务必保留本文链接及本版权信息。 #欢迎广大运维同仁一起交流linux/unix网站运维技术! #QQ:919953500#E-mail:weilandeshanhuhai126.com …

lucene api

设置重新打开索引目录&#xff08;清空&#xff09; IndexWriterConfig conf new IndexWriterConfig(new WhitespaceAnalyzer());conf.setOpenMode(OpenMode.CREATE);转载于:https://www.cnblogs.com/ydxblog/p/5688415.html

Linux计算求取文件长度

在文件IO的编程过程中&#xff0c;经常会遇到如何求取文件长度的问题&#xff0c;这里将工积累的一些常用方法写出来与大家分享。 第一类&#xff1a;lseek/fseek 1、lseek int GetFileSize&#xff08;char *_pName) {int iFd -1;int iLen 0;if (_pName NULL){return -…

[linux-svn] linux下svn常用指令

From: http://www.cnblogs.com/aLittleBitCool/archive/2011/07/09/2101602.html windows下的TortoiseSVN是资源管理器的一个插件&#xff0c;以覆盖图标表示文件状态&#xff0c;几乎所以命令都有图形界面支持&#xff0c;比较好用&#xff0c;这里就不多说。主要说说linux下…

微信小程序uni.switchTab传参获取不到;小程序跳转到tabBar页并传参;uni-app微信小程序获取tabBar页面参数失败;uni-app微信小程序tabBar页面onLoad不执行

需求场景&#xff1a;从非tabBar页面B跳转到tabBar页面A&#xff0c;并想要携带参数。 如果使用uni.switchTab传参&#xff0c;会导致tabBAE页面获取不到参数&#xff1b; 原因&#xff1a; 官方文档有说&#xff0c;uni.switchTab路径后不能带参数&#xff1b; uni.navigateT…

UIView的旋转iOS开发

更多阅读请访问http://www.hopean.com 有关UIView坐标变换的&#xff0c;但是经常不能得到自己想要的效果&#xff0c;今天就把它仔细研究了下。记下来等以后忘记的时候再复习 重写shouldAutorateToInterfaceOrientation:&#xff0c;限制某个方向会改变原点的位置&#xff0c;…

jsp中字段截取

jsp页面上截取 <c:choose><c:when test"${fn:length(ad.remark) > 80}"><c:out value"${fn:substring(ad.remark, 0, 80)}..." /></c:when><c:otherwise><c:out value"${ad.remark}"/></c:otherwise&…

OPENWRT使用命令行设置无线和有线网络

在我们将路由器固件刷成开源的基于Linux内核的openwrt系统后&#xff0c;由于openwrt默认未安装WEB管理界面&#xff0c;所以我们需要先通过SSH或者telnet对路由器进行网络设置&#xff0c;设置完成后可通过openwrt的软件包管理opkg安装web设置界面Luci。 设置lan ip(即访问路…

Linux下安装nginx, php, php-fpm并配置

环境&#xff1a; Fedora20 目标: 在Fedora20上安装好nginx服务器&#xff0c;并可正确解析php文件 1. 安装&#xff1a; yum makecache yum install nginx php php-fpm 2. nginx的配置 按默认配置(网站根目录路径&#xff1a;/usr/share/nginx/html)&#xff0c;一般也是OK…

Spring中Bean的生命中期与InitializingBean和DisposableBean接口

Spring提供了一些标志接口&#xff0c;用来改变BeanFactory中的bean的行为。它们包括InitializingBean和DisposableBean。实现这些接口将会导致BeanFactory调用前一个接口的afterPropertiesSet()方法&#xff0c;调用后一个接口destroy()方法&#xff0c;从而使得bean可以在初始…

uni-app微信小程序uni.navigateTo跳转无效问题;记录一次uni-app页面跳转无效,来回跳转问题;wx.navigateTo ,跳转超过10次怎么点不动的解决办法。

场景需求&#xff1a;从小程序A页面跳转到小程序B页面&#xff0c;然后B页面还可以跳到A页面。 跳转失效原因&#xff1a; –1.uni.navigateTo只能跳转到非tabBar页面&#xff0c;tabBar导航栏页面只能用uni.switchTab方法跳转&#xff1b; –2.uni.navigateTo跳转的页面栈太多…

VIM选择文本块/复制/粘贴

在正常模式下&#xff08;按ESC进入&#xff09;按键v进入可视化模式&#xff0c;然后按键盘左右键或h,l键即可实现文本的选择。 其它相关命令&#xff1a;   v&#xff1a;按字符选择。经常使用的模式&#xff0c;所以亲自尝试一下它。 V&#xff1a;按行选择。这在你想拷…

微信小程序tabBar导航栏页和其他页执行onLoad与onShow时机;tabBar页获取不到参数问题;navigateTo跳转无效问题;onShow执行两次问题;

1.注意点&#xff1a; 只有五种情况会触发导航栏tabBar页的onLoad函数&#xff0c;分别是&#xff1a; –1.1&#xff1a;首次进入到导航栏tabBar页面&#xff1b; –1.2&#xff1a;从微信分享进入的导航栏tabBar页面&#xff1b; –1.3&#xff1a;识别二维码跳转到小程序的导…

20160512关于mac安装caffe的记录

记得2015年在mac系统上安装过一次caffe&#xff0c;非常顺利&#xff0c;但是最近群里许多同学反映mac安装caffe出现了各种问题&#xff0c;同时我也在帮助别人安装caffe的时候也遇到了一些坑&#xff0c;不再像以前这么顺利了。估计与操作系统&#xff0c;caffe升级有关。 今晚…

tar打包时排除一些文件或者目录

From: http://www.blogjava.net/zhyiwww/archive/2012/08/23/386119.html 用tar打包时排除文件&#xff1a;可以使用-exclude参数如&#xff1a; tar cvfz backup_1.tar.gz /opt/data --excluderesource*在打包/opt/data时就排除了resource命名的目录和文件。如果想不包含部…

linux下tar.gz、tar、bz2、zip等解压缩、压缩命令小结

Linux下最常用的打包程序就是tar了&#xff0c;使用tar程序打出来的包我们常称为tar包&#xff0c;tar包文件的命令通常都是以.tar结尾的。生成tar包后&#xff0c;就可以用其它的程序来进 行压缩了&#xff0c;所以首先就来讲讲tar命令的基本用法&#xff1a;   tar命令的选…

去除多余的0;正则表达式:去掉数字(整数、小数)前面多余的零

方案1&#xff1a; 来源&#xff1a;http://zhidao.baidu.com/question/277893180.html如果是整数&#xff1a;去掉整数前的零 例如&#xff1a;0098765--->98765 如果是小数&#xff1a;去掉小数前的零 例如&#xff1a;000.333--->0.333 再例如000.00--->0.00<…

Python学习笔记(二)

2019独角兽企业重金招聘Python工程师标准>>> pickle #腌制 pickle.dump() #保存数据 pickle.load() #恢复数据 locals() # 返回当前作用域中的变量集合 with语句会自动处理所有已打开文件的关闭工作&#xff0c;类似C#中的using() >>> data[5,78,3,45,7,1]…