Linux - 进程等待和进程替换

news2024/12/12 12:14:36

进程等待

前面我们了解了如果父进程没有回收子进程, 那么当子进程接收后, 就会一直处于僵尸状态, 导致内存泄漏, 那么我们如何让父进程来回收子进程的资源.

waitpid

我们可以通过 Linux 提供的系统调用函数 wait 系列函数来等待子进程死亡, 并回收资源.

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);

wait函数用于等待任何一个子进程结束, 并回收其资源. 

  • status:指向整数的指针, 用于存储子进程的退出状态. 如果不需要这个信息, 可以传递NULL.
  • 成功时返回被等待的子进程的PID, 失败时返回 -1, 并设置 errno.

waitpid函数允许父进程等待特定的子进程结束

  • pid:子进程的PID. 如果为 -1, 则等待任何一个子进程。
  • status:同wait函数.
  • options:等待选项, 常用的有 WNOHANG (非阻塞等待). 
  • 成功时返回被等待的子进程的PID. 失败时返回-1,并设置errno

一般来说, 用 waitpid 多一点, 因为 waitpid 提供的更为细致的操作.

int main()    
{    
    pid_t id = fork();    
    if(id<0)    
    {    
        perror("fork");    
        exit(1);    
    }    
    if(id==0)//子进程代码    
    {    
        int count = 5;    
        while(count)    
        {    
            printf("[%d]我是子进程,我的pid是: %d\n",count,getpid());    
            sleep(1);    
            count--;    
        }    
        exit(0);//子进程执行完代码后退出, exit 会直接终止本进程                                                                        
    }                                                                                                    
    //父进程代码                                                                                         
    waitpid(id,NULL,0);                                                                                  
    printf("等待子进程成功!\n");                                                                         
    return 0;     
}

可以观察到, 在等待子进程结束之前, 父进程卡在了 waitpid(), 直到子进程都被等待成功, 父进程才会继续向后执行.

status 参数

在 wait 和 waitpid 函数中都存在一个 status 的参数.
在 status 中存储着子进程的退出码和退出信号.
如果父进程想要了解子进程的退出信息, 可以通过 status 来了解.

status 是一个 int 类型的变量, 一共有 32 个bit, 我们主要看它的低 16 位bit.

那么如何获取退出状态 (退出码) 和 信号

退出状态 (退出码):
(status >> 8) & 0xFF
退出信号:
status & 0x7F
int main()    
{    
    pid_t id = fork();    
    if(id<0)    
    {    
        perror("fork");    
        exit(1);    
    }    
    
    if(id==0)//子进程代码    
    {    
        int count = 5;    
        while(count)    
        {    
            printf("[%d]我是子进程,我的pid是: %d\n",count,getpid());    
            sleep(1);    
            count--;    
        }    
        exit(55);//子进程执行完代码后退出                                                                                                                               
    }    
    //父进程代码    
    int status = 0;    
    waitpid(id,&status,0);    
    printf("等待子进程成功!\n");    
    printf("进程退出码: %d,进程退出信号: %d\n",(status >> 8) & 0xFF,status & 0x7F);    
    return 0;    
}  

当子进程在运行时, 使用 kill -9 617714命令, 来终止子进程, 那么也就能观察到, 退出信号为 9.

option

在前面的参数解释中说到, 这是一个等待选项.
父进程可以选择一直阻塞下去, 直到等到子进程的死亡,
或者当子进程还没死亡时, 父进程去执行其他的代码. 等一会再来查看子进程是否死亡.

waitpid(pid,&status,WNOHANG); // 非阻塞等待
waitpid(pid,&status,0); // 阻塞等待

int main()
{
    pid_t id = fork();
    if(id<0)
    {
        perror("fork");
        exit(1);
    }

    if(id==0)//子进程代码
    {
        int count = 5;
        while(count)
        {
            printf("[%d]我是子进程,我的pid是: %d\n",count,getpid());
            sleep(1);
            count--;
        }
        exit(55);//子进程执行完代码后退出
    }
    //父进程代码
    while(1)//循环访问子进程退出情况
    {
        int wait = waitpid(id,NULL,WNOHANG);
        if(wait>0)//子进程退出成功
        {
            printf("子进程退出成功,子进程pid: %d\n",wait);
            break;
        }
        else if(wait==0)//子进程还没退出,父进程干自己的事情
        {
            //此处简单模拟父进程干的事情
            printf("我是父进程\n");
        }
        else //等待子进程退出失败
        {
            perror("waitpid");
            exit(1);
        }
        sleep(1);
    }
    return 0;
}

 执行上面的代码就能观察到, 在子进程结束前, 父进程还在向频幕上打印文字.

进程替换

创建子进程是为了完成一些工作, 但是子进程的代码和父进程是一样的,
有可能子进程并不需要执行父进程的代码, 而是执行一些其他代码.
这种场景下, 就可以使用进程替换.

我们为什么不直接将子进程要执行的代码写在父进程中呢, 还要去使用进程替换?

1. 如果所有的代码都放在父进程中, 那么父进程的代码会有多么的庞大,

这会提高代码编写和维护的成本. 

2. 进程所执行的一定是我们的C/C++程序吗? 进程也可以执行其他的非 C/C++ 程序,

那对于那些非 C/C++ 程序 (java程序), 我们无法将他们和我们所写的 C/C++ 代码整合在一起, java 和 C/C++ 的运行环境都不同.

exec 系列函数

如果想要创建出来的子进程执行全新的程序, 可以使用 exec 系列函数进行程序替换.

一共有6个函数, 其中主要分为两类
1. execl 系列
2. execv 系列

execl

int main()    
{    
    printf("进行程序替换了\n");    
    int n = execl("/usr/bin/ls","ls","-a","-l",NULL);                                                                                                                   
    if(n==-1)    
    {    
        perror("execl");    
    }    
    printf("程序替换完毕!\n");    
    return 0;    
}    

execl参数: 第一个是要执行程序的路径 (/usr/bin/ls),
第二个参数是要执行的程序的名称 (ls),

后面的参数到 NULL 之前, 都是要替换的程序参数 (-a, -l, 都是 ls 程序的参数).

execl 中 l, 表示如何将参数传递要替换的程序. l 表示通过一个列表的方式,
即向上面的 "-l", "-a"..., 一个列表的形式.

execlp 和 execle 两个函数则分别多了 p 和 e.

p 则代表要执行的程序可以从环境变量 PATH 中找到, 所以不用写执行程序的路径.

e 则表示, 可以传入用户自己定义的环境变量 (_env[]) 给程序使用.

int main()    
{    
    printf("我要进行程序替换了...\n");    
    int n = execlp("ls","-l",NULL);                                                                                                                                     
    if(n==-1)    
    {    
        perror("execl");    
    }    
    printf("程序替换完毕!\n");    
    return 0;    
} 

int main()    
{    
    const char* _env[]={"MY_ENV=666",NULL};    
    printf("我要进行程序替换了...\n");    
    int n = execle("/usr/bin/ls","ls","-l",NULL,_env);//自己定义一个环境变量MY_ENV=666传递给要去执行的程序                                                              
    if(n==-1)    
    {    
        perror("execl");    
    }    
    printf("程序替换完毕!\n");    
    return 0;    
}  

execv

上面的 execl 中的 l, 代表传参使用列表的形式.
那么 v 很容易就想到了是vector.
所以 execv 函数在给替换的程序传参时, 是通过一个 vector 来传参的.

int main()    
  {    
	  char* const set[]={"ls","-a","-l",NULL};  
      printf("我要进行程序替换了...\n");    
      int n = execv("/usr/bin/ls",set);                                                                             
      if(n==-1)    
      {    
          perror("execl");    
      }    
      printf("程序替换完毕!\n");    
      return 0;    
  } 

那么剩下的 execvp 和 execvpe 和之前的 execl 系列中的一样.
p 代表在环境变量 PATH 中查找, e 可以传入自己定义的环境变量.

  • l (list): 传参的方式为使用列表来传递
  • v (vector): 使用数组来传递参数
  • p (path): 会在环境变量 PATH 中查找程序
  • e (env): 可以传递自己定义的环境变量

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

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

相关文章

虚拟主机怎么选哪家的性价比高

选择虚拟主机不能只看价格&#xff0c;还要看质量和服务&#xff0c;稳定快速的虚拟主机再加上优质的售后服务&#xff0c;才可令网站顺利运行&#xff0c;站长才无后顾之忧。 选虚拟主机或云服务器还是要选大牌主机商的产品比较好&#xff0c;质量和服务更有保障&#xff0c;例…

英文论文翻译成中文,怎样翻译更地道?

我是娜姐 迪娜学姐 &#xff0c;一个SCI医学期刊编辑&#xff0c;探索用AI工具提效论文写作和发表。 最近学员群有同学问&#xff0c;英文论文翻译成中文的解决方案—“DeepL翻译出来的内容总是有点别扭&#xff0c;ChatGPT能翻译的地道一些吗&#xff1f;”。 正好有位刚加入的…

40分钟学 Go 语言高并发:服务监控与追踪

服务监控与追踪 一、知识要点总览 模块核心内容技术选型难度监控指标请求量、响应时间、错误率、资源使用Prometheus Grafana中链路追踪分布式调用链、性能瓶颈分析Jaeger, OpenTelemetry高日志处理日志收集、分析、存储ELK Stack中告警系统告警规则、通知渠道、告警分级Ale…

【Unity高级】如何实现粒子系统的间歇式喷射

先看下要最终实现的效果&#xff1a; 代码如下&#xff1a; using UnityEngine; using System.Collections;public class ParticleBurstController : MonoBehaviour {private ParticleSystem _particleSystem; // 获取粒子系统public float burstDuration 2f; // 每次…

clipchamp制作视频文字转语音音频

一.准备工作&#xff1a; 1.在浏览器打开 https://app.clipchamp.com/首次打开需要登录&#xff0c;未登录用户注册登录 2.点击右上角头像到Settings页面&#xff0c;点击Language切换到中文&#xff08;英文水平好的可以忽略此步骤&#xff09;因中文英文界面有微小差异&…

开源轻量级文件分享服务Go File本地Docker部署与远程访问

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

三菱伺服通过MR Configurator2进行的试运行模式

(1)试运行模式 (a)JOG运转 可以不使用伺服系统控制器执行J0G运行。请在解除强制停止的状态下使用。无论伺服0N/伺服OFF或伺服系统控制器有无连接均可使用。 通过MR Configurator2的J0G运行画面进行操作。 1)运行模式 2)运行方法 "“仅在长按正转、反转按钮中运行”的复选框…

Sqoop导入数据(mysql---->>hive)

目录 数据传输流程脚本报错和异常说明1. Caused by: java.lang.ClassNotFoundException: org.apache.hadoop.hive.conf.HiveConf2. 数据导入hive后显示NULL 数据传输流程 mysql---->>hdfs---->>hive 数据从mysql表中取出&#xff0c;放到hdfs上&#xff08;由targ…

Flask返回中文Unicode编码(乱码)解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

【C++AVL树】枝叶间的旋律:AVL树的和谐之道

公主请阅 1.AVL树的概念2.AVL树的插入AVL树插入一个值的大概过程平衡因子更新更新原则更新停止条件 3.AVL树的右转旋转的原则右单旋 4.AVL树的左旋左单旋 5.AVL树的左右双旋6.AVL树的右左双旋7.AVL树的模拟实现 1.AVL树的概念 AVL树是最先发明的自平衡二叉查找树&#xff0c;AV…

深入理解C#的TCPIP通信机制

本文还有配套的精品资源&#xff0c;点击获取 简介&#xff1a;在分布式系统和实时数据交换应用中&#xff0c;C#作为一种现代面向对象编程语言&#xff0c;利用其***命名空间下的Socket类&#xff0c;提供强大的TCP/IP通信功能。本文将探讨C#中TCP/IP通信的基本概念、使用方…

基于yolov8的SAR影像目标检测系统,支持图像、视频和摄像实时检测【pytorch框架、python源码】

更多目标检测、图像分类识别、目标追踪等项目可看我主页其他文章 功能演示&#xff1a; 基于yolov8的SAR影像目标检测系统&#xff0c;支持图像、视频和摄像实时检测【pytorch框架、python源码】_哔哩哔哩_bilibili &#xff08;一&#xff09;简介 基于yolov8的SAR影像目标…

Prime2_解法二:openssl解密凭据

Prime2_解法二&#xff1a;openssl解密凭据 本博客提供的所有信息仅供学习和研究目的&#xff0c;旨在提高读者的网络安全意识和技术能力。请在合法合规的前提下使用本文中提供的任何技术、方法或工具。如果您选择使用本博客中的任何信息进行非法活动&#xff0c;您将独自承担全…

Jenkins环境一站式教程:从安装到配置,打造高效CI/CD流水线环境-Ubuntu 22.04.5 环境离线安装配置 Jenkins 2.479.1

文章目录 Jenkins环境一站式教程&#xff1a;从安装到配置&#xff0c;打造高效CI/CD流水线环境-Ubuntu 22.04.5 环境离线安装配置 Jenkins 2.479.1一、环境准备1.1 机器规划1.2 环境配置1.2.1 设置主机名1.2.2 停止和禁用防火墙1.2.3 更新系统 二、安装配置Jenkins2.1 安装JDK…

K8S命令部署后端(流水线全自动化部署)

前言 本文为链接: 云效流水线k8s半自动部署java&#xff08;保姆级&#xff09;的补充,本文起初的目的是为了补充完善k8s流水线的全自动化部署,但是也适用于k8s的一键重启,因为使用k8s的web页面容易出现漏点的情况,因此也可以把代码保存为shell脚本,同样可以实现一键重启。关于…

力扣-图论-7【算法学习day.57】

前言 ###我做这类文章一个重要的目的还是给正在学习的大家提供方向和记录学习过程&#xff08;例如想要掌握基础用法&#xff0c;该刷哪些题&#xff1f;&#xff09;我的解析也不会做的非常详细&#xff0c;只会提供思路和一些关键点&#xff0c;力扣上的大佬们的题解质量是非…

TEA系列例题

解析 TEA 加密算法(C语言、python)&#xff1a;_tea加密-CSDN博客 CTF-RE 从0到N: TEA_tea加密原理-CSDN博客 1 字节 8 位 牢记密文的64位和密钥的128位,最好可以自己独立的写出tea解密代码 相当于密文是传入8个字符类型数据或者是2个整型数据, 密钥是16个字符数据或者4个…

首批|云轴科技ZStack成为信通院AI Cloud MSP技术服务实验室成员单位

近日&#xff0c;由全球数字经济大会组委会主办&#xff0c;中国信息通信研究院&#xff08;以下简称“中国信通院”&#xff09;、中国通信企业协会承办的云AI计算国际合作论坛在京举行&#xff0c;会上公布了AI Cloud MSP&#xff08;人工智能云管理服务提供商&#xff09;技…

Docker安装部署RabbitMQ

1. Docker环境准备 1.1 安装Docker 在开始Docker安装部署RabbitMQ之前&#xff0c;确保您的系统环境已经满足Docker的运行要求。以下是在不同操作系统上安装Docker的步骤和命令行演示。 对于Linux系统 在基于Debian的系统&#xff08;如Ubuntu&#xff09;上&#xff0c;您…

Linux下网卡实现NAT转发

目标 在嵌入式Linux设备下&#xff0c;使用单一的网卡&#xff08;前提支持STA&#xff0b;AP共存&#xff09;&#xff0c;使用NAT&#xff08;网络地址转换&#xff09;实现软路由&#xff0c;以自身为热点&#xff0c;将接收到的流量数据全部转发出去。 一&#xff0c;STA…