8.6 wait和waitpid函数-进程控制

当一个进程正常或异常终止时,内核就向其父进程发送SIGCHLD信号。因为子进程终止是个异步事件(这可以在父进程运行的任何时候发生),所以这种信号也是内核向父进程发的异步通知。父进程可以忽略该信号,或者提供一个该信号发生时即被调用执行的函数(信号处理程序)。对于这种信号的系统默认动作是忽略它。第1 0章将说明这些选择项。现在需要知道的是调用wait或waitpid的进程可能会:

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

• 带子进程的终止状态立即返回(如果一个子进程已终止,正等待父进程存取其终止状态)。

• 出错立即返回(如果它没有任何子进程)。

如果进程由于接收到SIGCHLD信号而调用wait,则可期望wait会立即返回。但是如果在一个任一时刻调用wait,则进程可能会阻塞。

#include <sys/types.h>

#include <sys/wait.h>

pid_t wait(int *statloc) ;

pid_t waitpid(pid_t pid, int *statloc, int options) ;

//两个函数返回:若成功则为进程I D,若出错则为-1

这两个函数的区别是:

• 在一个子进程终止前, wait 使其调用者阻塞,而waitpid 有一选择项,可使调用者不阻塞。

• waitpid并不等待第一个终止的子进程—它有若干个选择项,可以控制它所等待的进程。

如果一个子进程已经终止,是一个僵死进程,则wait立即返回并取得该子进程的状态,否则wait使其调用者阻塞直到一个子进程终止。如调用者阻塞而且它有多个子进程,则在其一个子进程终止时,wait就立即返回。因为wait返回终止子进程的进程I D,所以它总能了解是哪一个子进程终止了。

这两个函数的参数statloc是一个整型指针。如果statloc不是一个空指针,则终止进程的终止状态就存放在它所指向的单元内。如果不关心终止状态,则可将该参数指定为空指针。

依据传统,这两个函数返回的整型状态字是由实现定义的。其中某些位表示退出状态(正常返回),其他位则指示信号编号(异常返回),有一位指示是否产生了一个core文件等等。POSIX.1规定终止状态用定义在< sys / wait . h >中的各个宏来查看。有三个互斥的宏可用来取得进程终止的原因,它们的名字都以W I F开始。基于这三个宏中哪一个值是真,就可选用其他宏来取得终止状态、信号编号等。这些都在表8 - 1中给出。在8 . 9节中讨论作业控制时,将说明如何停止一个进程。

表8-1 检查wait和waitpid所返回的终止状态的宏

说明

WIFEXITED(status)

若为正常终止子进程返回的状态,则为真。对于这种情况可执行W EXIT STATUS(status) 取子进程传送给exit或_ exit参数的低8位

WIFSIGNALED(status)

若为异常终止子进程返回的状态,则为真(接到一个不捕捉的信号)。对于这种情况,可执行W T E R M S I G(status)取使子进程终止的信号编号。另外,S V R 4和4 . 3 + B S D(但是,非POSIX.1)定义宏:W CORE D U M P(status)若已产生终止进程的core文件,则它返回真

W I F S T O P P E D(status)

若为当前暂停子进程的返回的状态,则为真。对于这种情况,可执行WSTOPSIG(status)取使子进程暂停的信号编号

WIFCONTINUED(status)

若在作业控制暂停后已经继续的子进程返回了状态,则为真。

实例

程序8 - 3中的函数pr_exit使用表8 - 1中的宏以打印进程的终止状态。本章的很多程序都将调用此函数。注意,如果定义了W CORE D U M P,则此函数也处理该宏。

#include <stdio.h>

#include <sys/wait.h>

void pr_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%s\n",

                  WTERMSIG(status),

#ifdef  WCOREDUMP

                  WCOREDUMP(status) ? " (core file generated)" : "");

#else

"");

#endif

else if (WIFSTOPPED(status))

           printf("child stopped, signal number = %d\n",

                  WSTOPSIG(status));

}

程序8 - 4调用pr_exit函数,例示终止状态的不同值。运行程序8 - 4可得:

#include <stdio.h>

#include <stdlib.h>

#include <sys/wait.h>

void pr_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%s\n",

                  WTERMSIG(status),

#ifdef  WCOREDUMP

                  WCOREDUMP(status) ? " (core file generated)" : "");

#else

"");

#endif

else if (WIFSTOPPED(status))

           printf("child stopped, signal number = %d\n",

                  WSTOPSIG(status));

}

int main(void)

{

      pid_t pid;

int status;

if ((pid = fork()) < 0)

           perror("fork error");

else if (pid == 0)   /* child */

           exit(7);

if (wait(&status) != pid)    /* wait for child */

           perror("wait error");

      pr_exit(status); /* and print its status */

if ((pid = fork()) < 0)

           perror("fork error");

else if (pid == 0)   /* child */

           abort();      /* generates SIGABRT */

if (wait(&status) != pid)    /* wait for child */

           perror("wait error");

      pr_exit(status); /* and print its status */

if ((pid = fork()) < 0)

           perror("fork error");

else if (pid == 0)   /* child */

           status /= 0;     /* divide by 0 generates SIGFPE */

if (wait(&status) != pid)    /* wait for child */

           perror("wait error");

      pr_exit(status); /* and print its status */

      exit(0);

}

$ a.out

normal termination, exit status = 7

abnormal termination, signal number = 6 (core file generated)

abnormal termination, signal number = 8 (core file generated)

不幸的是,没有一种可移植的方法将W T E R M S I G得到的信号编号映射为说明性的名字。( 1 0 . 2 1节中说明了一种方法。)我们必须查看< s i g n a l . h >头文件才能知道S I G A B RT的值是6,S I G F P E的值是8。

正如前面所述,如果一个进程有几个子进程,那么只要有一个子进程终止, wait就返回。如果要等待一个指定的进程终止(如果知道要等待进程的I D ),那么该如何做呢?在早期的UNIX版本中,必须调用wait,然后将其返回的进程I D和所期望的进程I D相比较。如果终止进程不是所期望的,则将该进程I D和终止状态保存起来,然后再次调用wait。反复这样做直到所期望的进程终止。下一次又想等待一个特定进程时,先查看已终止的进程表,若其中已有要等待的进程,则取有关信息,否则调用wait。其实,我们需要的是等待一个特定进程的函数。P O S I X .1定义了waitpid函数以提供这种功能(以及其他一些功能)。

对于waitpid的pid参数的解释与其值有关:

pid == -1 等待任一子进程。于是在这一功能方面waitpid与wait等效。

pid > 0 等待其进程I D与pid相等的子进程。

pid == 0 等待其组I D等于调用进程的组I D的任一子进程。

pid < -1 等待其组I D等于pid的绝对值的任一子进程。( 9 . 4节将说明进程组。)

waitpid返回终止子进程的进程I D,而该子进程的终止状态则通过statloc返回。对于wait,其唯一的出错是调用进程没有子进程(函数调用被一个信号中断时,也可能返回另一种出错。第1 0章将对此进行讨论)。但是对于waitpid,如果指定的进程或进程组不存在,或者调用进程没有子进程都能出错。

options参数使我们能进一步控制waitpid的操作。此参数或者是0,或者是表8 - 2中常数的逐位或运算。

表8-2 waitpid的选择项常数

常数

说明

W N O H A N G

若由pid指定的子进程并不立即可用,则waitpid不阻塞,此时其返回值为0

W U N T R A C E D

若某实现支持作业控制,则由pid指定的任一子进程状态已暂停,且其状态自暂停以来还未报告过,则返回其状态。W I F S TO P P E D宏确定返回值是否对应于一个暂停子进程

WCONTINUED

若实现支持作业控制,那么由pid指定的任一个子进程在暂停后已经继续,但其状态尚未报告,则返回其状态。

waitpid函数提供了wait函数没有提供的三个功能:

a)     waitpid等待一个特定的进程(而wait则返回任一终止子进程的状态)。在讨论p o p e n函数时会再说明这一功能。

b)     waitpid提供了一个wait的非阻塞版本。有时希望取得一个子进程的状态,但不想阻塞。

c)     waitpid支持作业控制(以W U N T R A C E D选择项)。

实例

回忆一下8 . 5节中有关僵死进程的讨论。如果一个进程要fork一个子进程,但不要求它等待子进程终止,也不希望子进程处于僵死状态直到父进程终止,实现这一要求的诀窍是调用fork两次。程序8 - 5实现了这一点。

在第二个子进程中调用sleep以保证在打印父进程I D时第一个子进程已终止。在fork之后,父、子进程都可继续执行——我们无法预知哪一个会先执行。如果不使第二个子进程睡眠,则在fork之后,它可能比其父进程先执行,于是它打印的父进程I D将是创建它的父进程,而不是init进程(进程ID 1)。

#include <stdio.h>

#include <stdlib.h>

#include <sys/wait.h>

int main(void)

{

      pid_t pid;

if ((pid = fork()) < 0) {

           perror("fork error");

      } else if (pid == 0) {    /* first child */

if ((pid = fork()) < 0)

                 perror("fork error");

else if (pid > 0)

                 exit(0); /* parent from second fork == first child */

/*

            * We're the second child; our parent becomes init as soon

            * as our real parent calls exit() in the statement above.

            * Here's where we'd continue executing, knowing that when

            * we're done, init will reap our status.

            */

           sleep(2);

           printf("second child, parent pid = %d\n", getppid());

           exit(0);

      }

if (waitpid(pid, NULL, 0) != pid)    /* wait for first child */

           perror("waitpid error");

/*

       * We're the parent (the original process); we continue executing,

       * knowing that we're not the parent of the second child.

       */

      exit(0);

}

执行程序8 - 5得到:

$ a.out

$ second child, parent pid = 1

注意,当原先的进程(也就是exec本程序的进程)终止时, shell打印其指示符,这在第二个子进程打印其父进程I D之前。

转载于:https://www.cnblogs.com/shaoguangleo/archive/2011/10/19/2806016.html

8.6 wait和waitpid函数-进程控制相关推荐

  1. linux系统编程学习_(2)进程控制-- fork函数、exec函数族、回收子进程--孤儿进程僵尸进程、wait函数

    linux系统编程学习_(2)进程控制-- fork函数.exec函数族.回收子进程–孤儿进程僵尸进程.wait函数 进程控制 fork()函数 创建一个子进程. pid_t fork(void); ...

  2. 【Linux】4.0进程控制

    文章目录 Linux进程退出 Linux进程等待 wait( )函数 waitpid( )函数 进程程序替换 exec*函数系列 Makefile创建多个程序 mini_shell 内建命令和第三方命 ...

  3. 【Linux】进程控制2-进程等待

    文章目录 进程等待 进程等待的必要性 wait函数 waitpid函数 进程等待 进程等待的必要性 我们之前提到过僵尸进程,僵尸进程就是子进程先于父进程退出,子进程的退出状态信息发送给父进程但是父进程 ...

  4. UNIX高级环境编程(9)进程控制(Process Control)- fork,vfork,僵尸进程,wait和waitpid...

    本章包含内容有: 创建新进程 程序执行(program execution) 进程终止(process termination) 进程的各种ID 1 进程标识符(Process Identifiers ...

  5. wait/waitpid函数与僵尸进程、fork 2 times

    一.僵尸进程 当子进程退出的时候,内核会向父进程发送SIGCHLD信号,子进程的退出是个异步事件(子进程可以在父进程运行的任何时刻终止) 子进程退出时,内核将子进程置为僵尸状态,这个进程称为僵尸进程, ...

  6. UNIX环境编程学习笔记(21)——进程管理之获取进程终止状态的 wait 和 waitpid 函数...

    lienhua34 2014-10-12 当一个进程正常或者异常终止时,内核就向其父进程发送 SIGCHLD信号.父进程可以选择忽略该信号,或者提供一个该信号发生时即被调用的函数(信号处理程序).对于 ...

  7. linux的多进程等待,等待进程结束wait()和waitpid()函数

    上一节最后我们说到若子进程先于父进程结束时,父进程调用wait()函数和不调用wait()函数会产生两种不同的结果: --如果父进程没有调用wait()和waitpid()函数,子进程就会进入僵死状态 ...

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

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

  9. c语言进程waitpid,Linux下C语言开发(进程控制编程——wait()、waitpid())

    wait()和wait()函数说明 wait()函数用于使父进程(也就是调用wait()的进程)阻塞,直到一个子进程结束或该进程接收到一个指定的信号为止.如果该父进程没有子进程或它的子进程已经结束,则 ...

最新文章

  1. 利用WSS搭建学生作业平台
  2. MYSQL之SQL语句练习及思路_1
  3. 查询数据库表名,数据表信息,MySQL Key值(PRI, UNI, MUL)的含义
  4. Spring Boot 参考指南(运行你的应用程序)
  5. powerbi导入地图_Power BI系列教程之powerBI功能介绍及使用导引(一)
  6. 【Python基础知识-pycharm版】第十一节-文件操作(IO技术)
  7. Python高级——GIL全局解释器锁问题
  8. WEB交互的划时代革新--HTML5中WebSocket应用【1】
  9. Kubernetes的系统架构与设计理念
  10. ASP.NET控件Web CAD SDK发布v12版本,支持DWG 2018丨附下载
  11. Viewport的使用《转》
  12. ajax php断点调试,关于javascript:如何在jquery ajax调用期间调试php
  13. vim:the damn garbled of vim-devicons from nerdtree
  14. kali linux网络扫描~局域网扫描
  15. 友盟,听云统计到的crash如何定位
  16. 制作简单时钟logo
  17. 4K高清屏幕保护工具Aquarium 4K for Mac
  18. android高仿今日头条小视频转场切换效果
  19. 独立版旺店助手源码在线持续更新
  20. es多个字段排序_如何解决 ES 复杂聚合排序问题(嵌套桶排序)?

热门文章

  1. 史上最全Android开发中100%会用到的开源框架整理(1/5)
  2. 打开u盘时提示是否要将其格式化的提示
  3. SQL server 实例教程
  4. Oracle教程之四招提高Oracle位图索引的使用效果
  5. 程序员从初级到中级10个秘诀 【转载】
  6. 顽强的病毒,如何处理
  7. 第五课-第三讲05_03_bash脚本编程之二 条件判断
  8. js-ES6学习笔记-Iterator和for-of循环
  9. 分享mac磁盘清理的方法
  10. ORA-12919: Can not drop the default permanent tablespace