前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【Linux探索学习】第十八弹——进程等待:深入解析操作系统中的进程等待机制

【Linux探索学习】第十八弹——进程等待:深入解析操作系统中的进程等待机制

作者头像
GG Bond1
发布于 2024-12-04 00:34:37
发布于 2024-12-04 00:34:37
21300
代码可运行
举报
文章被收录于专栏:C/C++葵花宝典C/C++葵花宝典
运行总次数:0
代码可运行

Linux学习笔记:https://blog.csdn.net/2301_80220607/category_12805278.html?spm=1001.2014.3001.5482

前言:

在Linux操作系统中,进程是资源的管理和执行单元,每个进程都有其自己的生命周期。在进程的执行过程中,进程可能需要等待一些资源或事件的发生,例如等待I/O操作完成、等待信号、等待其他进程的结束等,这些都叫做进程等待。这些我们在前面讲进程状态的时候基本上都提到过,今天我们重点来讲解父进程等待子进程这类等待其它进程结束的问题

1. 父进程为什么要等待子进程

在前面上篇我们已经讲过僵尸状态的问题,子进程在执行结束后,如果父进程不及时进行接受处理,子进程就会进入僵尸状态,进入僵尸状态后,从而造成内存泄漏,而且kill -9信号也不能进行处理,同时我们的父进程也需要通过进程退出的方式来回收子进程资源,获取子进程退出信息,所以说进程等待十分有必要。

2. 父进程等待子进程的常用函数

Linux 提供了多个函数用于父进程等待子进程的结束:

函数名

描述

wait()

阻塞父进程,直到任一子进程退出,返回退出的子进程 PID。

waitpid()

更灵活的等待函数,可选择等待特定子进程,支持非阻塞模式。

waitid()

类似于 waitpid(),但功能更强大,支持更详细的选项。

signal() 和 sigaction()

注册 SIGCHLD 信号处理函数,用于非阻塞地获取子进程状态。

这四个函数中我们主要用到的是前两个函数,所以我们下面对前两个函数进行详细讲解


3. wait()waitpid() 函数详解
3.1 wait()

wait() 是最简单的等待子进程的函数,用法如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    pid_t pid = fork();
    if (pid == 0) {
        // 子进程
        printf("Child process running...\n");
        sleep(3);  // 模拟一些操作
        printf("Child process exiting...\n");
        exit(42);  // 退出码为 42
    } else {
        // 父进程
        int status;
        pid_t child_pid = wait(&status);  // 等待任意子进程结束
        if (WIFEXITED(status)) {  // 检查子进程是否正常退出
            printf("Child process %d exited with status %d\n", child_pid, WEXITSTATUS(status));
        }
    }
    return 0;
}

输出示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Child process running...
Child process exiting...
Child process 12345 exited with status 42

说明:

  • wait(&status) 阻塞父进程,直到有子进程退出。
  • 使用宏 WIFEXITEDWEXITSTATUS 分别检查子进程是否正常退出及其退出码。

补充:

  • 上面的例子中子进程只有一个,但有些时候我们可能有多个子进程,这个时候系统采用的是循环等待的方法来回收每一个子进程
  • 父进程在回收子进程时是随机的,也就是说当我们有多个子进程执行结束的时候,父进程先回收哪个子进程并不是确定的,是随机的,这也就是我们上面采用循环等待的原因
  • 循环等待的具体方法会在文章最后面的总结图里面给出示例

3.2 waitpid()

waitpid() 是更灵活的等待函数,支持:

  • 等待特定的子进程。
  • 非阻塞模式。

函数原型:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
pid_t waitpid(pid_t pid, int *status, int options);

参数

描述

pid

指定子进程的 PID,若为 -1 等待任意子进程,与pid等效;若大于1则等待其进程ID与pid相同的子进程

status

存储子进程的退出状态。

options

控制等待行为(如 WNOHANG 表示非阻塞)。

我们先对上面的表格做一个小补充:

参数option作用是控制等待行为,常见的等待方式主要有两种:阻塞等待和非阻塞等待

阻塞等待的意思就是在我们父进程等待子进程的过程中会进入阻塞状态,不会做其它的事情,直到子进程运行结束后再继续,而非阻塞等待则是不同的方式,非阻塞状态的父进程会在运行的过程中不断询问查看子进程的运行情况,当子进程运行结束时,会将结果反馈给父进程,但是在这个过程中父进程并不会停下来,它还会继续自己的执行

示例代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    pid_t pid1 = fork();
    if (pid1 == 0) {
        // 第一个子进程
        printf("Child 1 running...\n");
        sleep(2);
        printf("Child 1 exiting...\n");
        exit(1);
    }

    pid_t pid2 = fork();
    if (pid2 == 0) {
        // 第二个子进程
        printf("Child 2 running...\n");
        sleep(4);
        printf("Child 2 exiting...\n");
        exit(2);
    }

    // 父进程
    int status;
    pid_t child_pid;
    while ((child_pid = waitpid(-1, &status, 0)) > 0) {  // 等待所有子进程
        if (WIFEXITED(status)) {
            printf("Child %d exited with status %d\n", child_pid, WEXITSTATUS(status));
        }
    }
    return 0;
}

输出示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Child 1 running...
Child 2 running...
Child 1 exiting...
Child 12345 exited with status 1
Child 2 exiting...
Child 12346 exited with status 2

4. 使用 SIGCHLD 信号等待子进程

信号的知识我们在前面还没进行讲解,这里还是了解为主,感兴趣的可以看看,不懂的地方可以去搜一下:

SIGCHLD 信号在子进程状态发生变化时(如退出)发送给父进程。父进程可以注册信号处理函数来处理此信号。

代码示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>

void sigchld_handler(int sig) {
    int status;
    pid_t pid = wait(&status);  // 获取退出的子进程信息
    if (pid > 0 && WIFEXITED(status)) {
        printf("Child %d exited with status %d\n", pid, WEXITSTATUS(status));
    }
}

int main() {
    signal(SIGCHLD, sigchld_handler);  // 注册信号处理器

    pid_t pid = fork();
    if (pid == 0) {
        // 子进程
        printf("Child process running...\n");
        sleep(3);
        printf("Child process exiting...\n");
        exit(42);
    }

    // 父进程
    printf("Parent process doing other work...\n");
    while (1) {  // 模拟父进程的其他工作
        sleep(1);
    }
    return 0;
}

输出示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Parent process doing other work...
Child process running...
Child process exiting...
Child 12345 exited with status 42

5. 僵尸进程与避免方法

文章开头我们就已经讲过僵尸进程了,通过上面对进程等待的学习,再来看一下僵尸进程的概念,看看能不能加深理解

僵尸进程(Zombie Process) 是指子进程退出后,其退出信息尚未被父进程读取的状态。虽然僵尸进程不会占用CPU,但其占用的进程表项资源有限。

避免僵尸进程的方法:

  1. 确保父进程读取子进程状态:使用 wait()waitpid()
  2. 忽略 SIGCHLD 信号:通过 signal(SIGCHLD, SIG_IGN) 忽略信号。
  3. 使用守护进程(init 进程回收子进程):如果父进程终止,init 进程会自动接管并回收子进程。

6. 总结

父进程等待子进程是进程管理中的关键机制。在实际应用中:

  • 简单的任务可以使用 wait()
  • 更复杂的需求(如非阻塞、多子进程等待)推荐使用 waitpid()
  • 实时应用可以结合 SIGCHLD 信号处理。

合理地使用这些机制,不仅可以有效管理资源,还能避免僵尸进程的问题,提升程序的健壮性和运行效率。

本篇笔记:

感谢各位大佬观看,创作不易,还请各位大佬点赞支持!!!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-12-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【Linux】进程控制
在 linux 中 fork 函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
用户11290673
2025/05/20
1020
【Linux】进程控制
【Linux 进程控制】—— 进程亦生生不息:起于鸿蒙,守若空谷,归于太虚
在 Linux中 fork 函数是非常重要的函数,它从已存在进程中创建⼀个新进程。创建出来的新进程叫做子进程,而原进程则称为父进程。
换一颗红豆
2025/04/12
710
【Linux 进程控制】—— 进程亦生生不息:起于鸿蒙,守若空谷,归于太虚
【Linux】进程控制
fork常用法:1.一个父进程希望复制自己,使父子进程同时执行不同的代码段。2.一个进程要执行一个不同的程序。
平凡的人1
2023/10/15
2740
【Linux】进程控制
【进程控制】
父进程创建了子进程,子进程的PCB是拷贝的父进程的PCB,内容一致。并且数据段,代码段,子进程是与父进程共享的(此时权限会被设为只读),当子进程要对其进行修改,此时会发生写时拷贝,将子进程要修改的数据,拷贝一份,单独交给子进程(这一部分权限可写,可读)。
是预备程序员a
2025/04/28
820
【进程控制】
【Linux系统编程】七、进程等待
​ 之前我们讲过,子进程退出,父进程如果还在执行中,就会造成 “ 僵尸进程 ” 的问题,进而造成内存泄漏。另外,进程一旦变成僵尸状态,那就刀枪不入,“ 杀人不眨眼 ” 的 kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程。 最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,或者是否正常退出。父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息。
利刃大大
2025/02/18
1360
【Linux系统编程】七、进程等待
探索进程控制第一弹(进程终止、进程等待)
在linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
南桥
2025/05/22
880
探索进程控制第一弹(进程终止、进程等待)
【Linux】深入 Linux 进程等待机制:阻塞与非阻塞的奥秘
进程等待是多进程编程中至关重要的一部分,主要原因是为了让父进程正确管理子进程生命周期并避免各种问题。
Yui_
2024/10/20
3170
[操作系统] 进程等待
进程等待(Process Waiting)是操作系统中父进程用于管理和同步子进程的重要机制。根据你的图片内容,我们可以分几个部分来讲解进程等待的相关知识。
DevKevin
2025/02/12
2230
[操作系统] 进程等待
【Linux系统】探索进程等待与程序替换的奥秘
在 Linux 操作系统的世界里,进程是程序运行的动态实体,它们如同一个个忙碌的工作者,承载着系统中各种任务的执行。无论是系统服务的稳定运行,还是用户程序的交互响应,都离不开进程的支持。深入理解进程的生命周期,包括创建、终止、等待以及程序替换等关键环节,对于掌握 Linux 系统编程和开发高性能应用程序至关重要。
suye
2025/05/07
1280
【Linux系统】探索进程等待与程序替换的奥秘
Linux: 关于 SIGCHLD 的更多细节
对该机制有稍微了解的话,不难得知一个关键因素:SIGCHLD。正是这个SIGCHLD起到了通知的作用,所以后面的处理也是基于它而实现。
Lin_R
2018/10/22
3K0
Linux: 关于 SIGCHLD 的更多细节
【Linux进程控制】五、wait()函数——子进程回收
僵尸进程:子进程结束,父进程没有回收子进程的资源(PCB),这个资源必须要由父进程回收,否则就形成僵尸进程。
mindtechnist
2024/08/08
2300
【Linux进程控制】五、wait()函数——子进程回收
Linux:进程地址空间、进程控制(一.进程创建、进程终止、进程等待)
上次介绍了环境变量:Linux:进程概念(四.main函数的参数、环境变量及其相关操作)
是Nero哦
2024/05/25
3680
Linux:进程地址空间、进程控制(一.进程创建、进程终止、进程等待)
Linux-进程控制
  进程控制不仅仅是管理程序的执行顺序,还涉及到资源的分配等问题,那么话不多说,开始我们今天的话题!
用户11029129
2024/06/04
1400
Linux-进程控制
Linux wait() 和 waitpid()函数介绍
转载自http://blog.csdn.net/wallwind/article/details/6998602 ---- 当一个进程正常或异常终止的时候,内核就像其父进程发送SIGCHLD信号,因为子进程是个一步事件,所以这种信号也是内核系那个父进程发的异步通知。父进程可以选择忽略该信号,或者提供一个该信号发生时即被调用执行的函数。对于这种信号的系统默认动作是忽略它。 现在要知道调用wait或waitpid的进程可能会发生什么情况:  如果其所有子进程都在运行,则阻塞。 如果一个子进程已经终止,正在得带的
老白
2018/03/19
2.6K0
Linux wait() 和 waitpid()函数介绍
【Linux探索学习】第十七弹——进程终止:深入解析操作系统中的进程终止机制
https://blog.csdn.net/2301_80220607/category_12805278.html?spm=1001.2014.3001.5482
GG Bond1
2024/11/30
4350
【Linux探索学习】第十七弹——进程终止:深入解析操作系统中的进程终止机制
【Linux】Linux进程控制——进程创建、进程终止及进程等待详解
在Linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
P_M_P
2024/08/15
8670
【Linux】Linux进程控制——进程创建、进程终止及进程等待详解
Linux进程控制——Linux进程等待
前言:接着前面进程终止,话不多说我们进入Linux进程等待的学习,如果你还不了解进程终止建议先了解:
Eternity._
2024/06/14
9060
Linux进程控制——Linux进程等待
进程控制
在linux中fork函数是非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
海盗船长
2020/08/27
7690
【Linux】进程控制
在linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
秦jh
2024/09/24
1890
【Linux】进程控制
L010Linux和androidNDK之linux避免僵尸进程,子进程退出的处理
L010Linux和androidNDK之linux避免僵尸进程,子进程退出的处理
上善若水.夏
2018/09/28
3.3K0
推荐阅读
相关推荐
【Linux】进程控制
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验