Linux - 进程等待和进程替换

进程等待

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

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.mzph.cn/diannao/63439.shtml

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

相关文章

mac下载安装jdk

背景 长时间不折腾mac全部忘记 特此记录 安装 1.下载jdk 根据需要下载对应的jdk 我直接 下载到/Applicatiions目录 https://www.oracle.com/java/technologies/downloads/#java8-mac 2.解压 cd /Applicatiions tar -zxvf jdk-8u431-macosx-x64.tar.gz 3.配置环境 …

【Java】—— 图书管理系统

基于往期学习的类和对象、继承、多态、抽象类和接口来完成一个控制台版本的 “图书管理系统” 在控制台界面中实现用户与程序交互 任务目标&#xff1a; 1、系统中能够表示多本图书的信息 2、提供两种用户&#xff08;普通用户&#xff0c;管理员&#xff09; 3、普通用户…

1-1.mysql2 之 mysql2 初识(mysql2 初识案例、初识案例挖掘)

一、mysql2 概述 mysql2 是一个用于 Node.js 的 MySQL 客户端库 mysql2 是 mysql 库的一个改进版本&#xff0c;提供了更好的性能和更多的功能 使用 mysql2 之前&#xff0c;需要先安装它 npm install mysql2 二、mysql2 初识案例 1、数据库准备 创建数据库 testdb CREAT…

[HDCTF 2023]LoginMaster

[HDCTF 2023]LoginMaster 知识点 quine注入 解题 用户名要为admin 查看robots.txt&#xff0c;查看源码 password是注入点 function checkSql($s) {if(preg_match("/regexp|between|in|flag||>|<|and|\||right|left|reverse|update|extractvalue|floor|subs…

springboot398研究生调研管理系统(论文+源码)_kaic

摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对信息管理混乱&#xff0c;出错率高&#xff0c;信息安全性差&#…

OSCP - Proving Grounds - Zino

主要知识点 SMB知识python脚本提权 具体步骤 执行nmap Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-10-10 01:24 UTC Nmap scan report for 192.168.52.64 Host is up (0.00077s latency). Not shown: 65529 filtered tcp ports (no-response) PORT STATE SER…

JDK8新特性之Stream流03

收集Stream流中的结果 IntStream intStream = Stream.of(1, 2, 3, 4, 5).mapToInt(Integer::intValue); intStream.filter(n -> n > 3).forEach(System.out::println); intStream.filter(n -> n > 3).count; intStream.filter(n -> n > 3).reduce(0, Integer…

自制shell命令行解释器,深入理解Linux系统命令行实现原理

个人主页&#xff1a;敲上瘾-CSDN博客 个人专栏&#xff1a;Linux学习、游戏、数据结构、c语言基础、c学习、算法 目录 ​编辑 1.打印命令提示符 ​编辑 2.获取用户输入指令 3.重定向分析 4.命令行参数表与环境变量表 5.命令解析 6.命令执行 6.1.创建子进程 6.2.文件…

ADB常用各模块操作命令

目录 1. 基本设备信息获取 2. 设备连接与管理 3. 文件管理 4. 进程与应用管理 5. 日志与调试 6. 调试和性能 7. 设备操作 8.adb命令的应用场景 1. 基本设备信息获取 获取设备的系统版本&#xff0c;获取设备安卓版本号&#xff1a; adb shell getprop ro.build.version.…

Mac M1 安装数据库

1. Docker下载 由于Sqlserver和达梦等数据库&#xff0c;不支持M系列的芯片&#xff0c;所以我们通过docker安装 下载并安装docker: https://www.docker.com/get-started/ 安装完成后&#xff0c;打开docker 2. SQL Server 安装 2.1 安装 打开终端&#xff0c;执行命令 doc…

渗透测试实验环境搭建

下载虚拟机镜像 5个虚拟机镜像&#xff0c;其中Linux攻击机我选择用最新的kali Linux镜像&#xff0c;其余的均使用本书配套的镜像。 网络环境配置 VMware虚拟网络编辑器配置&#xff1a; 将VMnet1和VMnet8分别设置IP为192.168.10.0/24和10.10.10.0/24。 虚拟机镜像配置 攻击机…

Linux shell脚本(一)

监控内存和磁盘容量&#xff0c;小于给定值时报警 [rootlinux-lyz test1]# ./monitor.sh & [1] 23110 # 提取根分区剩余空间 disk_size$(df / | awk /\//{print $4})# 提取内存剩余空间 mem_size$(free | awk /Mem/{print $4}) while : do # 注意内存和磁盘提取空间大小都…

C# 中 Interface(接口)和 virtual(虚方法)

文章目录 前言一、Interface&#xff08;接口&#xff09;1. 什么是接口2. 接口的定义3. 实现接口4. 接口的作用 二、virtual&#xff08;虚方法&#xff09;1. 什么是虚方法2. 虚方法的定义3. 重写虚方法4. 虚方法的作用 三、Interface 和 virtual 的结合使用1. 接口中的虚方法…

JWT 在 SaaS 系统中的作用与分布式 SaaS 系统设计的最佳实践

在现代 SaaS&#xff08;软件即服务&#xff09; 系统中&#xff0c;随着服务规模的扩大和用户需求的多样化&#xff0c;如何高效、安全地进行用户身份验证、权限控制以及租户隔离&#xff0c;成为了系统架构中的核心问题之一。**JWT&#xff08;JSON Web Token&#xff09;**作…

《智能体雏形开发(高阶实操)》二、智能体雏形开发

基于阿里云百炼平台开发智能体应用:生成日报与周报 在智能体开发中,生成结构化的日报与周报是一个典型的任务。本篇文章将基于阿里云百炼平台,结合 Python 开发环境,介绍如何开发一个从日志文件提取信息并生成摘要的智能体。我们将从需求分析、任务设计到核心功能实现逐步…

阿里云ECS服务器域名解析

阿里云ECS服务器域名解析&#xff0c;以前添加两条A记录类型&#xff0c;主机记录分别为www和&#xff0c;这2条记录都解析到服务器IP地址。 1.进入阿里云域名控制台&#xff0c;找到域名 ->“解析设置”->“添加记录” 2.添加一条记录类型为A,主机记录为www&#xff0c…

Scala的正则表达式(1)

package hfd //正则表达式的应用场景 //1.查找 findAllin //2.验证 matches //3.替换//验证用户名十分合法 //规则&#xff1a; //1.长度在6-12之间 //2.不能数字开头 //3.只能包含数字&#xff0c;大小写字母&#xff0c;下划线 object Test36 {def main(args: Array[String])…

网络知识:IP数据报知识详解

目录 一、IP数据报概念 二、IPV4数据报报头组成 三、IPV6数据报报头组成 今天给大家分享IP数据库相关的知识,希望对大家进一步了解IP协议提供一些帮助! 一、IP数据报概念 TCP/IP协议的网际层接收到传输层传递过来的数据单元,封装成向下(OSI模型的数据链路层、TCP/IP协…

Android 因为混淆文件配置,打release包提示running R8问题处理

一、报错信息 Missing classes detected while running R8. Please add the missing classes or apply additional keep rules that are generated in E:\workplace\xxxxxx\app\build\outputs\mapping\release\missing_rules.txt. Missing class org.mediakit.R$layout (refer…

【中断】向量中断、中断服务程序、中断向量、中断向量表、中断向量地址,之间的关系

向量中断&#xff1a; 一种识别中断源的方式和技术。 中断服务程序&#xff1a; 不同的中断信号&#xff0c;需要用不同的 中断处理程序&#xff08;中断服务程序&#xff09; 来处理。中断服务程序一般是OS模块。 中断向量&#xff1a; 由向量地址形成部件&#xff0c;是…