linux18:进程等待

news2024/11/13 9:37:07

进程等待的必要性

1:子进程创建的目的是要完成父进程指派的某个任务,当子进程运行完毕退出时,父进程需要通过进程等待的方式,回收子进程资源,获取子进程退出信息(子进程有无异常?没有异常结果是否正确?检查退出码查看错误原因)
2:父进程如果一直不回收,就可能造成子进程‘僵尸进程’的问题,子进程一直得不到父进程的回收,进而造成内存泄漏。
3:子进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力杀死一个已经死去的进程。只能通过进程等待将他进行回收

子进程等待的两个方法  wait  与  waitpid

1:wait方法:

wait 函数用于等待一个已启动的子进程结束。这个函数通常与 fork 函数一起使用,fork 用于创建子进程。wait 函数会挂起(父进程阻塞等待)调用它的父进程,直到有一个子进程结束或到达指定的时间限制。

头文件

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

函数原型:

pid_t wait(int *status);

status:输出型参数,一个指向整数的指针,如果提供一个变量,子进程的结束状态将被存储在所指向的整数变量中

 子进程退出结束状态的三种情况:1:代码运行完毕,结果正确

                                                       2:代码运行完毕,结果不正确

                                                       3:异常终止

        退出码   (status>>8)&0xFF     终止信息    status&0x7F          

      返回值:

  • 成功时,wait 返回结束的子进程的进程ID。
  • 如果有错误发生,返回 -1,并设置 errno 以指示错误类型。

例子:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int main()
{
    pid_t pid;
    int status;

    pid = fork(); // 创建子进程

    if (pid == -1) // 返回值是1时,说明fork() 调用失败
    {
        perror("fork");
        exit(EXIT_FAILURE);
    }
    else if (pid == 0) // 子进程
    {
        printf("Child process, PID: %d, PPID: %d\n", getpid(), getppid());
        exit(EXIT_SUCCESS);
    }
    else  // 父进程 
    {
        printf("Parent process, PID: %d, PPID: %d\n", getpid(), getppid());
        pid = wait(&status);    // 挂起直到子进程结束
        if (pid == -1)     // 返回值是1时,说明wait() 调用失败
        {
            perror("wait");
            exit(EXIT_FAILURE);
        }
        if (WIFEXITED(status))  // 最后查看检查子进程的结束状态
        {
            printf("Child process exited with status %d\n", WEXITSTATUS(status));
        }
    }

    return 0;
}

这个例子中,父进程使用 fork 创建了一个子进程。

子进程打印出它的进程ID和父进程ID,然后退出。

父进程则调用 wait 函数并传入一个指向整数的指针,这个整数将存储子进程的结束状态。

因为有wait方法存在,一旦子进程结束,父进程将继续执行,并检查子进程的结束状态。

2:waitpid方法:

wait 函数不同,waitpid 允许父进程指定要等待的子进程的进程标识符(PID),这提供了更细粒度的控制。

waitpid 对于处理多个子进程的情况特别有用,因为它允许父进程等待特定的子进程或一组子进程。

头文件与wait文件相同

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

函数原型:

pid_t waitpid(pid_t pid, int *status, int options);
  • pid_t pid:指定要等待的子进程的 PID。
  • 如果 pid > 0,waitpid 等待特定的pid子进程。
  • 如果 pid == -1waitpid 等待任何子进程,类似于 wait 函数。
  • 如果 pid < -1,waitpid 等待进程组 ID(PGID)等于 pid 绝对值的任何子进程。
  • ---------------------------------------------------------------------------------------------------------------------------

  • int *status:一个指向整数的指针,如果提供一个变量,结束状态将被存储在所指向的整数变量中
  • WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
  • WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
  • ---------------------------------------------------------------------------------------------------------------------------
  • int options:指定 waitpid 调用的行为的选项,
  •  WNOHANG(非阻塞轮询,使等待非阻塞)
    pid 指定的子进程没有结束,则 waitpid() 函数返回 0 ,不予以等待。若正常结束,则返回该子进程的ID
  •  WUNTRACED(除了已结束的子进程,还报告已停止的子进程)。
  •  0 表示默认选项

  • ---------------------------------------------------------------------------------------------------------------------------

      返回值与wait一样:

  • 成功时,wait 返回结束的子进程的进程ID。
  • 如果有错误发生,返回 -1,并设置 errno 以指示错误类型。

例子:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int main()
{
    pid_t pid, wpid;
    int status;

    // 创建子进程
    pid = fork();
    if (pid == -1) 
    {
        perror("fork");
        exit(EXIT_FAILURE);
    }

    if (pid == 0)     // 子进程
    {
        printf("Child process, PID: %d, PPID: %d\n", getpid(), getppid());
        sleep(5);
        printf("Child process terminating\n");
        exit(EXIT_SUCCESS);
    }

    //  waitpid方法,父进程等待特定的子进程结束
    wpid = waitpid(pid, &status, 0); 
    if (wpid == -1)  
    {
        perror("waitpid");
        exit(EXIT_FAILURE);
    }

    printf("Child process exited with status %d\n", WEXITSTATUS(status));

    return 0;
}

在这个示例中,父进程创建了一个子进程,

一旦sleep(5);5s过后,子进程结束,waitpid 返回子进程的 PID,父进程可以检查子进程的退出状态。

阻塞轮询例子

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>

int main()
{
    pid_t pid;
    int status;

    pid = fork(); // 创建子进程
    if (pid < 0)  // fork 出错 
    {
        perror("fork failed");
        exit(EXIT_FAILURE);
    }
    else if (pid == 0)  // 子进程
    {
        printf("Child process, PID: %d\n", getpid());
        sleep(5); 
        exit(0); 
    }
    else   // 父进程
    {
        while (1) 
        {
            pid_t ret = waitpid(pid, &status, WNOHANG); // 非阻塞等待
            if (ret == 0) // 子进程未结束,继续轮询
            {
                printf("Child process is still running.\n");
                sleep(1); // 等待1秒后再次检查
            }
            else if (ret == pid)  // 子进程已结束
            {
                if (WIFEXITED(status)) 
                {
                    printf("Child process exited with status %d\n", WEXITSTATUS(status)); //提取子进程退出码
                }
                else if (WIFSIGNALED(status)) 
                {
                    printf("Child process terminated by signal %d\n", WTERMSIG(status));  //终止子进程信号
                }
                break; 
            }
            else  // waitpid 出错
            {
                perror("waitpid failed");
                exit(EXIT_FAILURE);
            }
        }
    }

    return 0;
}

多个进程等待

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>


//0--5s   子进程运行,父进程运行
//5--15s  子进程回收,父进程运行

int main()
{
    pid_t child_pid;
    int status;
    int i;

    for (i = 0; i < 5; ++i)  // 创建5个子进程
    {
        child_pid = fork();
        if (child_pid == -1) // fork() 调用失败
        {
            perror("fork");
            exit(EXIT_FAILURE);
        }

        if (child_pid == 0)  // 这是子进程,每个子进程运行5s
        {
            int n = 5;
            while (n--)
            {
                printf("Child process, PID: %d, PPID: %d\n", getpid(), getppid());
                sleep(1);
            }
            exit(EXIT_SUCCESS);
        }
    }

    for (i = 0; i < 5; ++i) 
    {
        if (wait(&status) == -1) //循环5次wait来等待创建的5个子进程
        {
            perror("wait");
            exit(EXIT_FAILURE);
        }
        // 检查子进程的结束状态
        if (WIFEXITED(status)) 
        {
            printf("Child process exited with status %d\n", WEXITSTATUS(status));
        }
    }
    sleep(10); //父进程等待10s
    return 0;
}

我们首先通过一个 for 循环调用 fork() 五次,创建五个子进程。

每个子进程都会打印出自己的 PID 和 PPID,然后调用 sleep(1) 以模拟执行任务,接着通过 exit() 函数正常退出。

父进程在创建了所有子进程之后,会进入另一个 for 循环,使用 wait() 函数等待每个子进程结束。

wait() 会挂起父进程,直到有一个子进程结束。一旦有子进程结束,wait() 返回,父进程会检查子进程的结束状态,如果子进程是正常结束的,会打印出子进程的退出状态码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1612170.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Cpp_SDay02

空山新雨后&#xff0c;天气晚来春 文章目录 前言一、No File&#xff01;&#xff01;&#xff01;二、preProcessing三、static的用意&#xff1a;四、signed number /unsigned number五、函数六、#pragma once总结 前言 看视频令人犯困&#xff0c; 一、No File&#xff01…

Vue3基本功能介绍

文章目录 Vue3组件中的模板结构可以没有根标签div组合式APIRefReactive函数回顾Vue2响应式Vue3实现响应式对比reactive和refSetup注意点计算属性与监听computedWatchWatchEffectVue3生命周期自定义hook函数toRef其他组合APIshallowReactiveshallowRefreadonly和shallowOnlyToRa…

SRIO系列-时钟逻辑与复位逻辑

一、前言 上一篇讲述了SRIO协议的基本概念&#xff0c;传输的HELLO帧格式、事务类型等&#xff0c;本篇说一下SRIO IP核的时钟关系。 基本的IP设置可以参考此篇文章&#xff1a;【高速接口-RapidIO】Xilinx SRIO IP 核详解-CSDN博客 二、时钟关系 PHY可以在两个时钟域上运行…

ubuntu下boa服务器编译运行

一.下载boa源码并解压 官网网站&#xff1a;BOA源码 点击箭头所指的位置即可下载 解压&#xff1a; tar -xvf boa-0.94.13.tar.gz 解压完成得到目录&#xff1a; 二.安装环境所缺依赖&#xff0c;否则编译会报错 sudo apt install bison sudo apt install flex 三.编译 1…

vue快速入门(三十五)组件通信-父传子

注释很详细&#xff0c;直接上代码 上一篇 新增内容 父组件传值子组件接收父组件传来的数据 源码 App.vue <template><div id"app"><!-- :item"item"为将item的值传递给MyTest组件 --><MyTest v-for"item in roles" :key&q…

【Linux开发 第七篇】权限

权限 Linux组权限修改权限 Linux组 在linux中的每个用户必须属于一个组&#xff0c;不能独立于组外 文件/目录 所有者 一般为文件的创建者&#xff0c;谁创建了该文件&#xff0c;就自然的成为了该文件的所有者 这一列即为文件的所有者 修改文件的所有者&#xff1a; chown…

【已解决】win10系统 Docker 提示Docker Engine stopped解决全过程记录

【已解决】win10系统 Docker 提示Docker Engine stopped解决全过程记录 一、检查服务是否开启 找到 【Docker Desktop Service】&#xff0c;然后&#xff0c;启动他&#xff1b; 你也可以直接设置为“自动” 找到服务&#xff0c;右键》属性》启动类型&#xff1a;自动》点击…

经典目标检测YOLOV1模型的训练及验证

1、前期准备 准备好目录结构、数据集和关于YOLOv1的基础认知 1.1 创建目录结构 自己创建项目目录结构&#xff0c;结构目录如下&#xff1a; network CNN Backbone 存放位置 weights 权重存放的位置 test_images 测试用的图…

Java多线程并发八股问题总结

目录 (1).Java中实现线程的方法(2).如何停止一个正在运行的线程(3).notify和notifyAll有什么区别(4).sleep()和wait()有什么区别(5).volatile是什么&#xff1f;可以保证有序性吗&#xff1f;(6).Thread类中的start()和run方法有什么区别&#xff1f;(7).为什么wait、notify、n…

面试经典150题——跳跃游戏 II

面试经典150题 day10 题目来源我的题解方法一 动态规划方法二 贪心 题目来源 力扣每日一题&#xff1b;题序&#xff1a;45 我的题解 方法一 动态规划 动态规划&#xff0c;当j位置可达i位置时&#xff1a;dp[i]Math.min(dp[i],dp[j]1); 时间复杂度&#xff1a;O( n 2 n^2 n…

今日早报 每日精选15条新闻简报 每天一分钟 知晓天下事 4月21日,星期日

每天一分钟&#xff0c;知晓天下事&#xff01; 2024年4月21日 星期日 农历三月十三 1、 商务部等10部门联合发文&#xff1a;进一步支持境外机构投资境内科技型企业。 2、 第二艘国产大型邮轮下坞搭载建造&#xff0c;预计2026年底前交付&#xff0c;中国邮轮开启批量化建造…

Python-VBA函数之旅-hash函数

目录 一、hash函数的定义&#xff1a; 二、hash函数的工作方式&#xff1a; ​三、hash函数的优缺点&#xff1a; 四、hash函数的常见应用场景&#xff1a; 1、hash函数&#xff1a; 1-1、Python&#xff1a; 1-2、VBA&#xff1a; 2、推荐阅读&#xff1a; 个人主页&…

Efficient Subclass Segmentation in Medical Images论文速读

Efficient Subclass Segmentation in Medical Images 摘要 随着医学图像分析的研究兴趣越来越细化&#xff0c;大量注释的成本也在上升。降低成本的一种可行方法是使用粗粒度超类标签进行注释&#xff0c;同时使用有限的细粒度注释作为补充。通过这种方式&#xff0c;细粒度的…

BAPI_BATCH_CHANGE:修改批次的特征值

文章目录 BAPI_BATCH_CHANGE&#xff1a;修改批次的特征值实现步骤定义变量获取对象/类等 获取已维护特性值新特性值更新 注意事项最终效果字段介绍 BAPI_BATCH_CHANGE&#xff1a;修改批次的特征值 现在有一个需求是要修改批次里面的某一个特征值&#xff0c;所以需要使用到B…

带头循环双向链表专题

1. 双向链表的结构 带头链表⾥的头节点&#xff0c;实际为“哨兵位”&#xff0c;哨兵位节点不存储任何有效元素&#xff0c;只是站在这⾥“放哨 的” “哨兵位”存在的意义&#xff1a; 遍历循环链表避免死循环。 2. 双向链表的实现 2.1双向链表结构 typedef int DataTyp…

适合各大资源网投稿html源码

源码介绍 适合各大资源网投稿html源码&#xff0c;源码由HTMLCSSJS组成&#xff0c;记事本打开源码文件可以进行内容文字之类的修改&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器里面&#xff0c;重定向这个界面 效果预览 源码下载 适合各大资源…

mac电脑mysql下载与安装

mysql下载地址 历史下载地址 MySQL :: Download MySQL Community Server (Archived Versions) mac 版下载 mac版本分为 Intel 处理器 和 M系列处理器。 从 8.0.26开始&#xff0c; mysql 支持M系列处理器。 以前的都只有Intel 处理器的。 Intel 处理器选择 x86_64 M 系列处理…

冯喜运:4.21黄金市场失去正常反应?下周黄金原油解析

【黄金消息面解析 】&#xff1a;周五(4月19日)&#xff0c;伊朗媒体似乎淡化了以色列袭击的影响&#xff0c;表明地缘政治风险降低&#xff0c;导致避险资产需求放缓&#xff0c;金价回吐涨幅。本周现货黄金价格上涨超2%。美国黄金期货收盘上涨0.7%&#xff0c;至2413.8美元。…

深入理解Java IO流:字节流

深入理解Java IO流&#xff1a;字节流 引言 在Java中&#xff0c;IO&#xff08;输入/输出&#xff09;操作是程序与外部世界交互的重要方式。 其中&#xff0c;File类是进行文件操作的基础&#xff0c;而字节流和字符流则是数据传输的两种主要方式。 本文将深入探讨这些概念及…

C# 自动填充文字内容到指定图片

目录 需求 开发运行环境 方法设计 实现代码 AddText方法 图片转Base64 调用示例 小结 需求 在我们的一些发布系统项目应用中&#xff0c;会经常发布一些链接图标&#xff0c;该图标基本上以模板背景为主&#xff0c;并填充项目文字内容。解决方式一般会让美工进行制作…