zoukankan      html  css  js  c++  java
  • 8.6 wait和waitpid函数进程控制

    8.6 waitwaitpid函数-进程控制

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

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

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

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

    如果进程由于接收到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 检查waitwaitpid所返回的终止状态的宏

    说明

    WIFEXITED(status)

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

    WIFSIGNALED(status)

    若为异常终止子进程返回的状态,则为真(接到一个不捕捉的信号)。对于这种情况,可执行W T E R M S I G(status)取使子进程终止的信号编号。另外,S V R 44 . 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)取使子进程暂停的信号编号

    WIFCONTINUEDstatus

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

    实例

    程序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的值是6S 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函数以提供这种功能(以及其他一些功能)

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

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

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

    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之前。
  • 相关阅读:
    HDU 1058 Humble Numbers
    HDU 1421 搬寝室
    HDU 1176 免费馅饼
    七种排序算法的实现和总结
    算法纲要
    UVa401 回文词
    UVa 10361 Automatic Poetry
    UVa 537 Artificial Intelligence?
    UVa 409 Excuses, Excuses!
    UVa 10878 Decode the tape
  • 原文地址:https://www.cnblogs.com/shaoguangleo/p/2806016.html
Copyright © 2011-2022 走看看