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.rhkb.cn/news/486836.html

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

相关文章

【Redis】壹 —— Redis 介绍

文章目录&#xff1a; 前言 一、认识Redis 1. Redis 用途 作为数据库 作为流引擎 二、服务端高并发分布式结构演变 1. 单机架构 2. 应用数据分离架构 3. 应用服务集群架构 4. 读写分离 / 主从分离架构 5. 冷热分离 —— 引入缓存 6. 分库分表 7. 微服务架构 8. …

D88【python 接口自动化学习】- pytest基础用法

day88 pytest运行参数-q -s -v 学习日期&#xff1a;20241203 学习目标&#xff1a;pytest基础用法 -- pytest运行参数-q -s -v&pytest使用ini配置指定运行参数 学习笔记&#xff1a; pytest -q 说明&#xff1a;简化控制台的输出 pytest -q .\testcases\test_reques…

22 网络编程:Go 语言如何通过 RPC 实现跨平台服务

在上一讲中&#xff0c;我为你讲解了 RESTful API 的规范以及实现&#xff0c;并且留了两个作业&#xff0c;它们分别是删除和修改用户&#xff0c;现在我为你讲解这两个作业。 删除一个用户比较简单&#xff0c;它的 API 格式和获取一个用户一样&#xff0c;但是 HTTP 方法换…

java八股-流量封控系统

文章目录 请求后台管理的频率-流量限制流量限制的业务代码UserFlowRiskControlFilter 短链接中台的流量限制CustomBlockHandler 对指定接口限流UserFlowRiskControlConfigurationSentinelRuleConfig 请求后台管理的频率-流量限制 根据登录用户做出控制&#xff0c;比如 x 秒请…

AI给我们生活带来了哪些便利?

在21世纪的科技浪潮中&#xff0c;人工智能&#xff08;AI&#xff09;如同一股不可阻挡的力量&#xff0c;正深刻地改变着我们的生活方式&#xff0c;从日常琐事到复杂决策&#xff0c;无一不渗透着AI的智慧之光。它不仅极大地提升了生活效率&#xff0c;还为我们开启了前所未…

在vue3里使用scss实现简单的换肤功能

实现的换肤功能&#xff1a;主题色切换、亮色模式和暗黑模式切换、背景图切换 主题色就是网站主色&#xff0c;可以配置到组件库上面&#xff1b;亮色模式又分为两种风格&#xff1a;纯白风格和背景图风格&#xff0c;不需要背景图的话可以删掉这部分逻辑和相关定义&#xff1b…

css实现圆周运动效果

在CSS中可以通过 keyframes 动画 和 transform 属性实现元素的圆周运动。以下是一个示例代码&#xff1a; 示例代码 <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content…

Python subprocess.run 使用注意事项,避免出现list index out of range

在执行iOS UI 自动化专项测试的时候&#xff0c;在运行第一遍的时候遇到了这样的错误&#xff1a; 2024-12-04 20:22:27 ERROR conftest pytest_runtest_makereport 106 Test test_open_stream.py::TestOpenStream::test_xxx_open_stream[iPhoneX-xxx-1-250] failed with err…

不一样的CSS(4)--icon图标系列之svg

序言 上一节内容我们讲解了如何利用css去画一个五角星&#xff0c;其中包括了使用svg的方法&#xff0c;有些小伙伴们对svg的使用不是很了解&#xff0c;那么本节内容我们主要来讲一下&#xff0c;关于svg标签的的使用。 目录 序言一、svg的介绍二、安装SVG扩展插件三、SVG基…

springSecurity认证流程

Spring Security 是spring家族中的一个安全管理框架。相比于另一个安全框架Shiro&#xff0c;它提供更丰富的功能和社区资源&#xff0c;但也较难上手。所以一般大项目用spring Security&#xff0c;小项目用Shiro。 一般web应用需要认证和授权&#xff0c;这也是spring Secur…

FastAPI解决跨域报错net::ERR_FAILED 200 (OK)

目录 一、跨域问题的本质 二、FastAPI中的CORS处理 1. 安装FastAPI和CORS中间件 2. 配置CORS中间件 3. 运行FastAPI应用 三、解决跨域报错的步骤 四、案例:解决Vue.js与FastAPI的跨域问题 1. Vue.js前端应用 2. FastAPI后端API 3. 配置CORS中间件 4. 运行和测试 五…

react跳转传参的方法

传参 首先下载命令行 npm react-router-dom 然后引入此代码 前面跳转的是页面 后面传的是你需要传的参数接参 引入此方法 useLocation()&#xff1a;这是 react-router-dom 提供的一个钩子&#xff0c;用于获取当前路由的位置对象location.state&#xff1a;这是从其他页面传…

C++(十二)

前言&#xff1a; 本文将进一步讲解C中&#xff0c;条件判断语句以及它是如何运行的以及内部逻辑。 一&#xff0c;if-else,if-else语句。 在if语句中&#xff0c;只能判断两个条件的变量&#xff0c;若想实现判断两个以上条件的变体&#xff0c;就需要使用if-else,if-else语…

【Keil5教程及技巧】耗时一周精心整理万字全网最全Keil5(MDK-ARM)功能详细介绍【建议收藏-细细品尝】

&#x1f48c; 所属专栏&#xff1a;【单片机开发软件技巧】 &#x1f600; 作  者&#xff1a; 于晓超 &#x1f680; 个人简介&#xff1a;嵌入式工程师&#xff0c;专注嵌入式领域基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f496; 欢迎大家&#xff1…

三菱CNC数采超详细,资料全备教程,后续更新发那科数采教程

三菱数采详细教程 文章目录 三菱数采详细教程一、介绍1.背景2.需要掌握知识3.需要资料①三菱SDK包&#xff1a;A2②三菱com接口文档③C#代码&#xff1a;④VStudio⑤资料存放网盘 二、程序运行1.调试设备①条件②命令 2.运行软件①打开软件②运行程序 三、数据采集1.代码了解2.…

一文了解模式识别顶会ICPR 2024的研究热点与最新趋势

简介 对模式识别研究领域前沿方向的跟踪是提高科研能力和制定科研战略的关键。本文通过图文并茂的方式介绍了ICPR 2024的研究热点与最新趋势&#xff0c;帮助读者了解和跟踪模式识别的前沿研究方向。本推文的作者是黄星宇&#xff0c;审校为邱雪和许东舟。 一、会议介绍 ICPR…

在 Windows WSL 上部署 Ollama 和大语言模型:从镜像冗余问题看 Docker 最佳实践20241208

&#x1f6e0;️ 在 Windows WSL 上部署 Ollama 和大语言模型&#xff1a;从镜像冗余问题看 Docker 最佳实践 ⭐ 引言 随着大语言模型&#xff08;LLM&#xff09;和人工智能技术的迅猛发展&#xff0c;开发者们越来越多地尝试在本地环境中部署模型进行实验。 但部署过程中常…

混合云策略在安全领域受到青睐

Genetec 发布了《2025 年物理安全状况报告》&#xff0c;该报告根据超过 5,600 名该领域领导者&#xff08;其中包括 100 多名来自澳大利亚和新西兰的领导者&#xff09;的回应&#xff0c;揭示了物理安全运营的趋势。 报告发现&#xff0c;澳大利亚和新西兰的组织采用混合云策…

juc并发编程(下)

一些辅助类 减少计数CountDownLatch 设置一个计数器&#xff0c;通过countDown方法进行减1操作&#xff0c;使用await方法等待计数器不大于0&#xff0c;继续执行await方法之后的语句。 当一个或多个线程调用await方法时&#xff0c;这些线程会阻塞 其他线程调用countDown方…

调用matlab用户自定义的function函数时,有多个输出变量只输出第一个变量

很多朋友在使用matlab时&#xff0c;会使用或自己编辑多个function函数&#xff0c;来满足自己对任务处理的要求&#xff0c;但是在调用function函数时&#xff0c;会出现这个问题&#xff1a;调用matlab用户自定义的function函数时&#xff0c;有多个输出变量只输出第一个变量…