编程过程中,有时需要让一个进程等待另一个进程,最常见的是父进程等待自己的子进程,或者父进程回收自己的子进程资源包括僵尸进程。这里简单介绍一下系统调用函数:wait()

函数原型是

#include <sys/types.h>

#include <wait.h>

int wait(int *status)

函数功能是:父进程一旦调用了wait就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。

注:

  当父进程忘了用wait()函数等待已终止的子进程时,子进程就会进入一种无父进程的状态,此时子进程就是僵尸进程.

  wait()要与fork()配套出现,如果在使用fork()之前调用wait(),wait()的返回值则为-1,正常情况下wait()的返回值为子进程的PID.

  如果先终止父进程,子进程将继续正常进行,只是它将由init进程(PID 1)继承,当子进程终止时,init进程捕获这个状态.

  参数status用来保存被收集进程退出时的一些状态,它是一个指向int类型的指针。但如果我们对这个子进程是如何死掉毫不在意,只想把这个僵尸进程消灭掉,(事实上绝大多数情况下,我们都会这样想),我们就可以设定这个参数为NULL,就像下面这样:

pid = wait(NULL);

如果成功,wait会返回被收集的子进程的进程ID,如果调用进程没有子进程,调用就会失败,此时wait返回-1,同时errno被置为ECHILD。

  如果参数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,这个值就毫无意义。

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <wait.h>
#include <errno.h>
#include <stdlib.h>
/***********************************************************功能说明:进程等待wait()方法的应用author: linux.sir@qq.com***********************************************************/
void waitprocess();int main(int argc, char * argv[])
{waitprocess();}void waitprocess()
{int count = 0;pid_t pid = fork();int status = -1;if(pid<0){printf("fork error for %m\n",errno );}else if(pid>0){printf("this is parent ,pid = %d\n",getpid() );wait(&status);//父进程执行到此,马上阻塞自己,直到有子进程结束。当发现有子进程结束时,就会回收它的资源。}else{printf("this is child , pid = %d , ppid = %d\n",getpid(),getppid() );int i;for (i = 0; i < 10; i++) {count++;sleep(1);printf("count = %d\n", count)  ;}exit(5);}printf("child exit status is %d\n", WEXITSTATUS(status));//status是按位存储的状态信息,需要调用相应的宏来还原一下printf("end of program from pid = %d\n",getpid() );}

运行结果如下:

waitpid系统调用在Linux函数库中的原型是:

#include <sys/types.h> /* 提供类型pid_t的定义 */

#include <sys/wait.h>

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中只支持WNOHANG和WUNTRACED两个选项,这是两个常数,可以用"|"运算符把它们连接起来使用,比如:

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

}

返回值和错误

waitpid的返回值比wait稍微复杂一些,一共有3种情况:

1、当正常返回的时候,waitpid返回收集到的子进程的进程ID;

2、如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;

3、如果调用中出错,则返回-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);}/* 如果是父进程 */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);}elseprintf("some error occured/n");}

编译并运行:

$ cc 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秒钟的工作。父子进程都有工作要做,父进程利用工作的简短间歇察看子进程的是否退出,如退出就收集它。

提示:可以尝试在最后一个例子中把pr=waitpid(pc, NULL, WNOHANG); 改为pr=waitpid(pc, NULL, 0);或者pr=wait(NULL);看看运行结果有何变化?(修改后的结果使得父进程将自己阻塞,直到有子进程退出为止!)

原文链接:https://blog.csdn.net/wyhh_0101/article/details/83933308

Linux中wait()函数及waitpid()函数相关推荐

  1. Linux系统调用之wait,waitpid函数(进程相关函数)

    前言 如果,想要深入的学习Linux系统调用中的wait,waitpid函数,还是需要去自己阅读Linux系统中的帮助文档. 具体输入命令: man 2 wait/waitpid 即可查阅到完整的资料 ...

  2. linux c之wait和waitpid函数的用法和总结

    1.wait和waitpid函数的介绍 1)  wait()函数用于使父进程(也就是调用wait()的进程)阻塞,直到一个子进程结束或者该进程接收到了一个指定的信号为止.如果该父进程没有子进程或者它的 ...

  3. linux waitpid实例,【Linux】僵尸进程,孤儿进程以及wait函数,waitpid函数(有样例,分析很详细)...

    本文内容: 1.僵尸进程,孤儿进程的定义,区别,产生原因,处理方法 2.wait函数,waitpid函数的分析,以及比较 背景:由于子进程的结束和父进程的运行是一个异步的过程,即父进程永远无法预测子进 ...

  4. wait()函数和waitpid()函数详解

    文章目录 1. wait()函数 2. waitpid()函数 3. wait() 和 waitpid() 用法和比较 1. wait()函数 #include <sys/types.h> ...

  5. linux中以A开头的函数使用方式历程及详解

    A开头的Linux C函数 abort 异常终止程序 abort函数在调用的时候,会触发SIGABRT信号 #include <stdlib.h> #include <signal. ...

  6. linux基础知识——wait函数和waitpid函数回收子进程

    1.wait函数回收子进程 \qquad父进程可以调用wait()函数回收子进程的终止信息.wait函数有三个功能: \qquad阻塞等待子进程退出 \qquad回收子进程残留资源 \qquad获取子 ...

  7. #includeunistd.h存在linux中,含有系统服务的函数

    #include<unistd.h> linux标准库#include <unistd.h>与windows的#include <windows.h>(C语言开发) ...

  8. Linux中main和初启函数,main 中的 argv和argc 到底是个啥意思?

    原标题:main 中的 argv和argc 到底是个啥意思? 前言 一般我们平时写main函数的话,一般都是写不带参数的比较多,而且也习惯了这样写:其实标准的形式写法,main函数是带两个参数的,这两 ...

  9. Unix/Linux中的read和write函数

    文件描述符 对于内核而言,所有打开的文件都通过文件描述符引用.文件描述符是一个非负整数.当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符.当读或写一个文件时,使用open或creat ...

最新文章

  1. 基于交换技术的网络中,全双工主要运行在?( 内有答案与详解)
  2. 用JS脚本进行页面元素控制
  3. 读书笔记《Hadoop开源云计算平台》
  4. Codeforces757E.Bash Plays With Functions(积性函数 DP)
  5. C++ explicit关键字
  6. λ-矩阵(λ-矩阵在初等变换下的标准形)
  7. 倒序查10条数据_餐饮业总营收增量七成由外卖拉动,天眼查数据显示今年我国新增相关企业超10万家...
  8. 全局光照技术解析Global Illumination Explained
  9. ArcSDE工作机制
  10. python perl shell_Shell Perl Python 介绍
  11. 算法不会,尚能饭否之树(1)
  12. c语言报数函数问题,[编程入门]报数问题-题解(C语言代码)
  13. 如何注册Google Voice账号(电话号码)
  14. LCD显示屏的器件选择和驱动电路设计
  15. 红帽 linux 安装,linux (红帽)如何安装?
  16. Android微信App 分享功能调整,Android 微信SDK分享功能(1)
  17. Pandas 学习手册中文第二版:1~5
  18. Mybatis中的OGNL使用总结=
  19. 3D 霍尔效应位置传感器原理解析
  20. mysql 取年月_MySQL 获取指定时间段的年月

热门文章

  1. [Linux] 权限与指令间的关系
  2. ReactNative开发环境
  3. 提示错误:“应为“providerInvariantName”参数的非空字符串。”
  4. 题目1457:非常可乐(广度优先遍历BFS)
  5. 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(44)-工作流设计-设计表单...
  6. 万网与阿里巴巴业务关系图解
  7. SQL SERVER最大用户连接数
  8. 东北农业大计算机排名,黑龙江高校排名更新,东北林大排名第3,东油排名第8...
  9. centos 重启网卡_CentOS6 网络管理之网卡配置及简单路由设置
  10. java1a2b3c4d5e6f_用两个线程,一个输出字母,一个输出数字,交替输出1A2B3C4D...26Z...