[操作系统] 进程等待

进程等待(Process Waiting)是操作系统中父进程用于管理和同步子进程的重要机制。根据你的图片内容,我们可以分几个部分来讲解进程等待的相关知识。


为什么需要进程等待?

当一个进程创建了子进程后,子进程会独立运行。如果父进程不进行等待,它可能会比子进程更早终止,导致子进程变成僵尸进程(Zombie Process),即进程已经终止但父进程没有回收它的资源,造成内存泄漏。

另外,进程⼀旦变成僵⼫状态,那就⼑枪不⼊,“杀⼈不眨眼”的kill -9 也⽆能为⼒,因为谁也没有办法杀死⼀个已经死去的进程。

我们需要知道⽗进程派给⼦进程的任务完成的如何。如,子进程是否完成,结果是否正确。

因此,父进程通过进程等待的⽅式,回收⼦进程资源,获取⼦进程退出信息


进程等待的方式

进程等待主要有两种方式:

  1. pid_t **wait**(int *status);** 函数**
  2. pid_t **waitpid**(pid_t pid, int *status, int options);** 函数**

wait 函数

#include <sys/types.h>
#include <sys/wait.h>

pid_t wait(int *status);
  • wait 会阻塞父进程,直到任意一个子进程退出。
  • 返回退出的子进程的 PID。
  • status 指向一个整数变量,用于存储子进程的退出状态。

示例代码

int status;
pid_t pid = wait(&status);

这行代码会让父进程暂停执行,直到有一个子进程结束,并将其退出状态存储到 status 变量。


waitpid 函数

pid_t waitpid(pid_t pid, int *status, int options);
  • waitpid 提供比 wait 更灵活的进程等待方式。
  • pid 参数决定了要等待哪个子进程:
    • pid > 0:等待指定的子进程。
    • pid == -1:等待任意子进程(等价于 wait)。
    • pid == 0:等待与当前进程同组的任何子进程。
    • pid < -1:等待特定进程组的子进程。
  • options 参数:
    • 0:默认阻塞等待子进程退出。
    • WNOHANG:非阻塞模式,如果没有子进程退出,则立即返回 0
    • WUNTRACED:等待子进程停止(如 Ctrl+Z)。
    • WCONTINUED:等待已经继续执行的子进程(如 SIGCONT 信号)。

示例代码

int status;
pid_t pid = waitpid(-1, &status, 0);

此代码与 wait 类似,等待任意子进程结束。


如何获取子进程的退出状态?

waitwaitpid参数中的int *status是输出型参数,当做位图来看待,通过传入变量地址然后经过函数处理后得到对应结果,传入NULL则表示不关心子进程退出状态信息,否则操作系统会通过该参数将子进程的退出信息返回给父进程。

int有四字节,32比特位, waitpid 返回的 status 值只使用了 低 16 位(2 字节) 来存储进程的退出状态或终止信号信息。

具体的信息存储如图所示。

  1. 正常终止:如果进程是 **exit(n)** 退出的,
    • 高 8 位存储 exit(n) 返回的值 n
    • 低 7 位全 0,表示不是被信号终止的。
  2. 如果进程是被信号终止的
    • 低 7 位存储终止信号编号(如 SIGKILL=9)。
    • 第 7 位(bit 7)表示是否生成 core dump。(后续讲解)
    • 高 8 位无意义。

也就是说:

  • 正常退出:status = (exit_code << 8)
  • 信号终止:status = signal_number | (core_dump_flag << 7)

代码示例:

#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{
    pid_t pid;
    if ( (pid=fork()) == -1 )
        perror("fork"),exit(1);
    
    if ( pid == 0 )
    {
        sleep(20);
        exit(10);
    } 
    else 
    {
        int st;
        int ret = wait(&st);
        if ( ret > 0 && ( st & 0X7F ) == 0 )
        { 
            // 正常退出
            printf("child exit code:%d\n", (st>>8)&0XFF);
        } 
        else if( ret > 0 ) { 
            // 异常退出
            printf("sig code : %d\n", st&0X7F );
        }
    }
}

测试结果:

# ./a.out #等20秒退出 
child exit code:10 
# ./a.out #在其他终端kill掉 
sig code : 9

信息的宏解析

子进程退出时,status 变量会存储一些信息,可以使用以下宏解析:

  • WIFEXITED(status):如果子进程正常退出,返回非零值。
    • WEXITSTATUS(status):获取子进程的退出码。
  • WIFSIGNALED(status):如果子进程是被信号终止的,返回非零值。
    • WTERMSIG(status):获取终止子进程的信号编号。
  • WIFSTOPPED(status):如果子进程因 SIGSTOP 进入暂停状态,返回非零值。
    • WSTOPSIG(status):获取导致暂停的信号编号。

示例代码

if (WIFEXITED(status)) 
{
    printf("Child exited with status %d\n", WEXITSTATUS(status));
} 
else if (WIFSIGNALED(status)) 
{
    printf("Child terminated by signal %d\n", WTERMSIG(status));
}

非阻塞等待

waitpid()参数中WNOHANG 选项可用于非阻塞模式,即如果没有子进程退出,则 waitpid 立即返回 0,不会阻塞父进程。

示例代码

while (1) 
{
    int status;
    pid_t pid = waitpid(-1, &status, WNOHANG);
    
    if (pid > 0) 
    {
        printf("Child %d exited\n", pid);
    } 
    else if (pid == 0) 
    {
        printf("No child exited yet\n");
    }
    sleep(1); // 避免 CPU 过载
}

进程等待的示意图

  1. 父进程调用 wait/waitpid 等待子进程结束
  2. 子进程执行任务后终止
  3. 父进程回收子进程资源,避免僵尸进程


进程等待的应用场景

  • 防止僵尸进程:确保子进程结束后,父进程回收其资源。
  • 管理多个子进程waitpid 可以指定等待特定的子进程,适用于多子进程管理。
  • 避免阻塞主进程:使用 WNOHANG 选项,使主进程可以继续执行其他任务。

总结

  • wait 适用于简单场景,等待任意子进程结束。
  • waitpid 提供更精细的控制,支持非阻塞模式。
  • WIFEXITED 等宏可以解析子进程的退出状态。
  • WNOHANG 选项可以实现非阻塞等待,防止父进程卡死。
评论 45
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DevKevin

你们的点赞收藏是对我最大的鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值