linux18:进程等待

进程等待的必要性

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() 返回,父进程会检查子进程的结束状态,如果子进程是正常结束的,会打印出子进程的退出状态码。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

研究发现:提示中加入数百个示例显著提升大型语言模型的性能

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

人工智能时代的关键技术:深入探索向量数据库及其在AI中的应用

文章目录 1. 理解向量数据库&#xff1a;二维模型示例2. 向量数据库中的数据存储与检索3. 向量数据库如何工作&#xff1f;4. 向量数据库如何知道哪些向量相似&#xff1f; 在人工智能技术日益成熟的当下&#xff0c;向量数据库作为处理和检索高维数据的关键工具&#xff0c;对…

LlamaIndex 加 Ollama 实现 Agent

AI Agent 是 AIGC 落地实现的场景之一&#xff0c;与 RAG 不同&#xff0c;RAG 是对数据的扩充&#xff0c;是模型可以学习到新数据或者本地私有数据。AI Agent 是自己推理&#xff0c;自己做&#xff0c;例如你对 AI Agent 说我要知道今天上海的天气怎么样&#xff0c;由于 AI…

WSL2无法ping通本地主机ip的解决办法

刚装完WSL2的Ubuntu子系统时&#xff0c;可能无法ping通本地主机的ip&#xff1a; WSL2系统ip&#xff1a; 本地主机ip&#xff1a; 在powershell里输入如下的命令&#xff1a; New-NetFirewallRule -DisplayName "WSL" -Direction Inbound -InterfaceAlias &quo…

AI大模型探索之路-认知篇4:大语言模型预训练基础认知

文章目录 前言一、预训练流程分析二、预训练两大挑战三、预训练网络通信四、预训练数据并行五、预训练模型并行六、预训练3D并行七、预训练代码示例总结 前言 在人工智能的宏伟蓝图中&#xff0c;大语言模型&#xff08;LLM&#xff09;的预训练是构筑智慧之塔的基石。预训练过…

【简单讲解下如何学习C++】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

微信小程序开发工具的使用,各个配置文件详解,小程序开发快速入门

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

网页信息提取能力哪家强?GPT4、Claude、perplexity、kimi、通义千问大比拼

barnesandnoble网上书店有一个页面&#xff1a;https://www.barnesandnoble.com/b/books/step-into-reading-early-readers-kids-fiction/step-into-reading-book-series-a-step-3-book-childrens-fiction/_/N-29Z8q8Z2i94?Nrpp40&page1 &#xff0c; 现在想把网页上的书名…

【Linux高性能服务器编程】两种高性能并发模式剖析——半同步/半异步模式

hello &#xff01;大家好呀&#xff01; 欢迎大家来到我的Linux高性能服务器编程系列之两种高性能并发模式介绍&#xff0c;在这篇文章中&#xff0c;你将会学习到高效的创建自己的高性能服务器&#xff0c;并且我会给出源码进行剖析&#xff0c;以及手绘UML图来帮助大家来理解…

分布式与一致性协议之拜占庭将军问题(三)

拜占庭将军问题 叛将先发送消息 如果是叛将楚先发送作战消息&#xff0c;干扰作战计划&#xff0c;结果会有所不同吗&#xff1f; 在第一轮作战信息协商中&#xff0c;楚向苏秦发送作战指令"进攻",向齐、燕发送作战指令"撤退"&#xff0c;如图所示(当然还…

【勒索病毒恢复】.svh勒索病毒介绍及恢复方案

一、.[[backupwaifu.club]].svh勒索病毒介绍 svh勒索病毒是一种恶意软件&#xff0c;它通过加密受害者的文件并要求支付赎金来解锁&#xff0c;从而达到勒索的目的。这种病毒已经存在了数年&#xff0c;并且不断演变&#xff0c;形成了多种不同的家族和变种。如果您的数据承载着…

接口测试-笔记

Date 2024年4月23日21:19:51 Author KarrySmile 1. 前言 因为想更加规范地开发接口&#xff0c;同时让自己测试接口的时候更加高效&#xff0c;更好地写好接口文档。所以学习黑马的《接口自动化测试》课程。链接&#xff1a;黑马程序员软件测试接口自动化测试全套视频教程&a…

Maven基础篇6

Idea环境中资源上传与下载 具体问题本地仓库如何与私服打交道&#xff1b; 本地仓库向私服上传文件&#xff0c;上传的文件位置在哪里&#xff1f; 访问私服配置相关信息&#xff1a;用户名密码&#xff1b; 下载东西&#xff0c;需要的各种信息&#xff0c;需要的仓库组的…

TDengine高可用探讨

提到数据库&#xff0c;不可避免的要考虑高可用HA&#xff08;High Availability&#xff09;。但是很多人对高可用的理解并不是很透彻。 要搞清高可用需要回答以下几个问题&#xff1a; 什么是高可用&#xff1f;为什么需要高可用&#xff1f;高可用需要达到什么样的目标&am…

【面试经典 150 | 数组】反转字符串中的单词

文章目录 写在前面Tag题目来源解题思路方法一&#xff1a;模拟实现方法二&#xff1a;使用库函数 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并附带一些对于本…

公园景区伴随音乐系统-公园景区数字IP广播伴随音乐系统建设指南

公园景区伴随音乐系统-公园景区数字IP广播伴随音乐系统建设指南 由北京海特伟业任洪卓发布于2024年4月23日 随着“互联网”被提升为国家战略&#xff0c;传统行业与互联网的深度融合正在如火如荼地展开。在这一大背景下&#xff0c;海特伟业紧跟时代步伐&#xff0c;凭借其深厚…

Security用户管理(一)

Security初探(三)-CSDN博客 Security的身份验证流程: AuthenticationFilter拦截请求并将身份验证职能委托给AuthticationManager.为了实现身份验证逻辑,AuthticationManager会使用身份验证程序.为了检查用户名和密码,AuthenticationProvider会使用UserDetailsService和Passwor…

爱上JDK源码阅读-枚举类

在日常开发中&#xff0c;经常会用到枚举类。这篇文章主要探讨一下枚举类和普通类有什么区别&#xff0c;以及编译过程中偷偷做了什么事情。 知识点 枚举类的本质编译器对枚举类的改动 先看一段简单的枚举类代码&#xff1a; enum StatusType {ON(1) ,OFF(2);StatusType(int …

mongodb 安装问题

1. mongodb启动时显示 Illegal instruction (core dumped) mongodb 5.0之后(包括5.0) 开始使用需要使用 AVX 指令集 2.启动时报错 ERROR: child process failed, exited with 1 通过指令 bin/mongod --repair 查看报错信息 根据报错信息进行修改 3. 配置服务器添加节点时…

Ubuntu20.04安装redis5.0.7

redis下载命令&#xff1a; wget https://download.redis.io/releases/redis-5.0.7.tar.gz 解压到 opt目录下 tar -zxvf redis-5.0.7.tar.gz -C /opt apt install -y gcc # 安装gccapt install make # 安装make 后面执行make一直报错 make报错后清除&#xff1a; make …