了解C语言中的wait()系统调用

(根据参考文档翻译整理而成,原文有修改)

wait()

    对 wait() 的调用会阻止调用进程,直到它的一个子进程退出或收到信号为止。子进程终止后,父进程在wait系统调用指令后继续执行。
    子进程可能由于以下原因而终止:

  • 调用exit();
  • 接收到main进程的return值;
  • 接收一个信号(来自操作系统或另一个进程),该信号的默认操作是终止。
    在这里插入图片描述
语法:
// 获取子进程退出状态并返回死掉的子进程ID 
pid_t wait(int *stat_loc);

    如果任何进程有多个子进程,则在调用 wait() 之后,如果没有子进程终止,则父进程必须处于wait状态。
    如果只有一个子进程被终止,那么 wait() 返回被终止的子进程的进程ID。
    如果多个子进程被终止,那么 wait() 将获取任意子进程并返回该子进程的进程ID。
    wait的目的之一是通知父进程子进程结束运行了,它的第二个目的是告诉父进程子进程是如何结束的。wait返回结束的子进程的PID给父进程。父进程如何知道子进程是以何种方式退出的呢?
    答案在传给wait的参数之中。父进程调用wait时传一个整型变量地址给函数。内核将子进程的退出状态保存在这个变量中。如果子进程调用exit退出,那么内核把exit的返回值存放到这个整数变量中;如果进程是被杀死的,那么内核将信号序号存放在这个变量中。这个整数由3部分组成,8个bit记录子进程exit值,7个bit记录信号序号,另一个bit用来指明发生错误并产生了内核映像(core dump)。
    如果进程没有子进程,那么 wait() 返回“-1”。
    示例:

// C program to demonstrate working of wait() 
#include<stdio.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<unistd.h>

int main()
{
    
    
    pid_t cpid;
    int status;
    int high_8, low_7, bit_7;

    if (fork()== 0)
    {
    
       
        printf("this is child process, the id is %d\n", getpid());
        exit(18);                                       /* terminate child */
    }
    else
    {
    
    
        printf("status is %d\n", status);
        cpid = wait(&status);                           /* reaping parent */
        high_8 = status >> 8;                           /* 1111 1111 0000 0000 */
        low_7  = status & 0x7F;                         /* 0000 0000 0111 1111 */
        bit_7  = status & 0x80;                         /* 0000 0000 1000 0000 */

        printf("status is %d\n", status);
        printf("high_8 is %d, low_7 is %d, bit_7 is %d\n", high_8, low_7, bit_7);
    }
    printf("Parent pid = %d\n", getpid()); 
    printf("Child pid = %d\n", cpid); 
  
    return 0; 
} 

    输出:

status is 0
this is child process, the id is 5412
status is 4608
high_8 is 18, low_7 is 0, bit_7 is 0
Parent pid = 5411
Child pid = 5412

    在本例中,wait() 将子进程的退出状态存储到status变量中,4608的二进制格式为0001 0010 0000 0000,前8位用10进制表示为18,也就是子进程exit的值。

WIF宏

    wait报告的子进程的状态信息不仅仅是子进程的退出状态,也包括:

  • 正常/异常终止
  • 终止原因
  • 退出状态

    有关状态的信息,请使用WIF….系列的宏。

  1. WIFEXITED(status):子级正常退出
    WEXITSTATUS(status):子级退出时的返回代码
  2. WIFSIGNALED(status):子级异常退出
    WTERMSIG(status):给出终止信号的代码
  3. WIFSTOPPED(status):子级被暂停。
    WSTOPSIG(status):给出暂停信号的代码。
/*if we want to prints information about a signal */
void psignal(unsigned sig, const char *s);

    示例:

// C program to demonstrate working of wait() 
#include<stdio.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<unistd.h>

int main()
{
    
    
    pid_t cpid;
    int status;
    int high_8, low_7, bit_7;

    if (fork()== 0)
    {
    
       
        printf("this is child process, the id is %d\n", getpid());      
        exit(18);                                       /* terminate child */
    }
    else
    {
    
    
        printf("status is %d\n", status);
        cpid = wait(&status);                           /* reaping parent */
        high_8 = status >> 8;                           /* 1111 1111 0000 0000 */
        low_7  = status & 0x7F;                         /* 0000 0000 0111 1111 */
        bit_7  = status & 0x80;                         /* 0000 0000 1000 0000 */

        printf("status is %d\n", status);
        printf("high_8 is %d, low_7 is %d, bit_7 is %d\n", high_8, low_7, bit_7);
        if (WIFEXITED(status))
                printf("the child process exit status is %d\n", WEXITSTATUS(status));
        else if (WIFSIGNALED(status))
                psignal(WTERMSIG(status), "Exit signal");
    }
    printf("Parent pid = %d\n", getpid()); 
    printf("Child pid = %d\n", cpid); 
  
    return 0; 
} 

    输出:

status is 0
this is child process, the id is 2997
status is 4608
high_8 is 18, low_7 is 0, bit_7 is 0
the child process exit status is 18
Parent pid = 2996
Child pid = 2997

    在上一个示例的基础上进行修改,可以看到,WEXITSTATUS(status) 的值就是子进程exit的值18,WEXITSTATUS() 简化了high_8的实现。

waitpid()

    我们知道如果有多个子进程被终止,那么 wait() 将收获任意子进程,但是如果我们想收获任何特定的子进程,我们将使用 waitpid() 函数。

语法:
pid_t waitpid (child_pid, &status, options);

    options参数:

  • 如果options为0表示没有选项,则父级必须等待子级终止;
  • 如果options为WNOHANG,表示如果子进程不终止,父进程不等待,只需检查并返回waitpid() 。(不阻塞父进程)
  • 如果child_pid为 -1,则表示任意子级,这里的 waitpid() 工作方式与 wait() 工作方式相同。

    waitpid() 的返回值:

  • 如果子级退出,返回子级的pid
  • 如果使用 WNOHANG或子级未退出,返回0。
// C program to demonstrate waitpid() 
#include<stdio.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<unistd.h>

void waitexample()
{
    
    
    int i, stat;
    pid_t pid[5];
    for (i=0; i<5; i++)
    {
    
    
        if ((pid[i] = fork()) == 0)
        {
    
    
            sleep(1);  
            printf("pid is %d\n", getpid());
            exit(100 + i); 
        } 
        else
        {
    
    
            printf("i is %d, the pid[i] is %d, mypid is %d\n", i, pid[i], getpid());
        }
    } 
  
    // Using waitpid() and printing exit status 
    // of children. 
    for (i=0; i<5; i++) 
    {
    
     
        pid_t cpid = waitpid(pid[i], &stat, 0); 
        if (WIFEXITED(stat)) 
            printf("Child %d terminated with status: %d\n", 
                   cpid, WEXITSTATUS(stat)); 
    } 
} 
  
// Driver code 
int main() 
{
    
     
    waitexample(); 
    return 0; 
} 

    输出:

i is 0, the pid[i] is 3554, mypid is 3553
i is 1, the pid[i] is 3555, mypid is 3553
i is 2, the pid[i] is 3556, mypid is 3553
i is 3, the pid[i] is 3557, mypid is 3553
i is 4, the pid[i] is 3558, mypid is 3553
pid is 3558
pid is 3557
pid is 3554
Child 3554 terminated with status: 100
pid is 3556
pid is 3555
Child 3555 terminated with status: 101
Child 3556 terminated with status: 102
Child 3557 terminated with status: 103
Child 3558 terminated with status: 104

    由输出可知,父进程3553创建了5个子进程,并等待子进程运行结束后退出,其中子进程执行顺序是随机的。

参考文档

[1]Kadam Patel.Wait System Call in C[EB/OL].https://www.geeksforgeeks.org/wait-system-call-c/,2019-11-01.

猜你喜欢

转载自blog.csdn.net/zsx0728/article/details/113772731