当前位置: 首页 > news >正文

《Linux从练气到飞升》No.19 进程等待

🕺作者: 主页

我的专栏
C语言从0到1
探秘C++
数据结构从0到1
探秘Linux
菜鸟刷题集

😘欢迎关注:👍点赞🙌收藏✍️留言

🏇码字不易,你的👍点赞🙌收藏❤️关注对我真的很重要,有问题可在评论区提出,感谢阅读!!!

目录

前言

在操作系统中,进程等待是一种关键的机制,用于实现进程之间的同步和协作。通过等待子进程的结束并获取其退出状态,父进程可以控制程序的执行顺序和处理子进程的结果。本篇博客将介绍进程等待的原理和用法,帮助读者深入理解进程间通信的重要概念和技术。

进程等待必要性

  • 之前讲过,子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。
  • 另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程。
  • 最后,父进程派给子进程的任务完成的如何,我们需要知道。如果,子进程运行完成,结果对还是不对,或者是否正常退出。
  • 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息

模拟僵尸进程

在我们讲述进程状态的时候,我们讲述过僵尸进程指的是:子进程退出,父进程不管不顾

模拟代码:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>int code = 0;int main()
{pid_t id = fork();if(id < 0){perror("fork");exit(1); //标识进程运行完毕,结果不正确}else if(id == 0){//子进程int cnt = 5;while(cnt){printf("cnt: %d, 我是子进程, pid: %d, ppid : %d\n", cnt, getpid(), getppid());sleep(1);cnt--;}}else{//父进程printf("我是父进程, pid: %d, ppid: %d\n", getpid(), getppid());sleep(7);}
}

运行结果:

 查看状态的bash命令:

while :; do ps ajx | head -1 && ps ajx | grep mycode | grep -v grep; sleep 1; echo "-----------------------"; done

查看状态:

 模拟成功!

进程等待的方法

子进程被创建出来,谁先运行,是有调度器说了算的。

那么谁先退出呢? 一般而言,我们通常要让子进程先退出。

为甚?

因为父进程可以很容易对子进程进行管理(垃圾回收)、处理业务,需要让父进程帮我们拿到子进程执行的结果。

一般子进程是需要被等待的,被父进程等,wait/waitpid.
 

wait方法

是什么?

是父进程通过wait等系统调用,用来等待子进程状态的一种现象,是必须的

为什么?

1.防止子进程发生僵尸问题,进而产生内存泄漏

2.读取子进程状态

怎么办?

wait/waitpid, status (signal, exit code).
 

#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*status);
返回值:
成功返回被等待进程pid,失败返回-1。
参数:
输出型参数,获取子进程退出状态,不关心则可以设置成为NULL

 参数:
        输出型参数:将wait函数内部计算的结果通过status返回给调用者
        输入型参数:调用者给被调用函数的传参


输入输出型参数编码的时候,小小的代码规范
        输入型:给引用
        输入输出,输出型参: 给指针

测试代码

#include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>int main()
{pid_t pid = fork();if (pid < 0){printf("fork error\n");}else if (pid == 0){int count = 0;while (1){sleep(1);printf("i am child\n");if (count == 3){break;}count++;}exit(0);}else{int count = 0;while (1){sleep(1);printf("i am father\n");while (count == 5){wait(NULL);}count++;}exit(0);}return 0;
}

运行结果

 

waitpid方法

pid_ t waitpid(pid_ _t pid, int *status, int options) ;
        pid :
                Pid=-1,等待任一个子进程。与wait等效。
                Pid>0,等待其进程ID与pid相等的子进程。
.
        status :同wait
        options :
                0 :阻塞模式
                WNOHANG :非阻塞 模式
                        非阻塞模式需要搭配循环使用

pid_ t waitpid(pid_t pid, int *status, int options);
返回值:
当正常返回的时候waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
参数:
pid:
Pid=-1,等待任一个子进程。与wait等效。
Pid>0.等待其进程ID与pid相等的子进程。
status:
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
options:
WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进
程的ID。
  • 如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。
  • 如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。
  • 如果不存在该子进程,则立即出错返回。

测试代码

#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>int main()
{pid_t pid = fork();if (pid < 0){printf("fork error!\n");}else if (pid == 0){//childint count = 0;while (count < 5){printf("child is running, pid=%d\n", getpid());sleep(1);count++;}exit(0);}else{//fatherprintf("father wait before!\n");pid_t ret = waitpid(pid, NULL, 0);if (ret > 0){printf("wait success!\n");}else{printf("wait failed\n");}printf("father wait after!\n");}return 0;
}

运行结果

看下面结果图发现当父进程调用了waitpid函数时父进程就被阻塞了,阻塞期间当子进程运行完毕父进程才执行完毕,所以只有子进程退出了父进程才会退出,那么子进程就一定不是僵尸进程。

 获取子进程status

pid_ t waitpid(pid_t pid, int *status, int options);
status:是一个整形指针,其实在传参的时候,该参数是一个输出型参数!


int st=0;
waitpid(pid, &st, 0); //开始等待,子进程退出,操作系统就会从进程PCB中读取退出信息,保存在status指向的变量中

返回之后,st中就保存的是我们进程退出的信息,int 是32bit,是否正常运行,退出码
是多少,退出信号是多少。
 

  • wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
  • 如果传递NULL,表示不关心子进程的退出状态信息。
  • 否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
  • status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特位):
    • 次低8位表示子进程退出码
    • 最低7个比特位表示进程收到的信号

//测试代码:
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main( void )
{pid_t pid;if ( (pid=fork()) == -1 )perror("fork"),exit(1);if ( pid == 0 ){sleep(20);exit(10);} else {int st;int ret = wait(&st);if ( ret > 0 && ( st & 0X7F ) == 0 ){ // 正常退出printf("child exit code:%d\n", (st>>8)&0XFF);} else if( ret > 0 ) { // 异常退出printf("sig code : %d\n", st&0X7F );}}    
}

 测试exit code,exit signal

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>int main()
{pid_t pid = fork();if (pid < 0){printf("fork error!\n");}else if (pid == 0){//childint count = 0;while (count < 5){printf("child is running, pid=%d\n", getpid());sleep(1);count++;}exit(0);}else{//fatherprintf("father wait before\n");int st = 0;pid_t ret = waitpid(pid, &st, 0);if (ret > 0){printf("wait success!\n");printf("st=%d\n", st);printf("child exit signal=%d\n", st & 0x7f);printf("child exit code=%d\n", (st >> 8) & 0xff);}if (st & 0x7F){printf("child run error!\n");}else{int code = (st >> 8) & 0xff;if (code){printf("child run success, but result is not right: code=%d\n", code);}else{printf("child run success, and result is right: code=%d\n", code);}}}printf("wait after!\n");return 0;
}

 

1.父进程通过wait/waitpid可以拿到子进程的退出结果,为什么要用wait/waitpid函数呢?直接全局变量不行吗?

进程具有独立性,那么数据就要发生写时拷贝,父进程无法拿到,况且,信号呢? ?

2. 既然进程是具有独立性的,进程退出码,不也是子进程的数据吗? ?父进程又凭什么拿到呢? ?wait/waitpid究竟干了什么呢? 

首先要知道僵尸进程至少要保留该进程的PCB信息!

task_struct里面保留了任何进程退出时的退出结果信息。

wait/waitpid 本质其实是读取子进程的task_struct结构 ,

task_struct 里面包含了: 【int exit_ code, exit_ signal;】

3.wait/waitpid有这个权利吗?

有,可以系统调用! ,不就是操作系统吗! ! task_ struct 是内核数据结构对象! !

阻塞与非阻塞

  • 阻塞等待是指一个任务在等待某个操作完成时,会被挂起,暂停执行直到操作完成后再继续执行。在阻塞等待期间,该任务无法进行其他的工作。
  • 非阻塞等待是指一个任务在等待某个操作完成时,会使用轮询或回调的方式不断查询操作状态,可以继续执行其他任务。非阻塞等待不会让一个任务暂停执行,即使操作未完成。
  • 两者的区别在于任务在等待某个操作完成时的行为表现:
    • 阻塞等待会暂停任务的执行,直到操作完成。
    • 非阻塞等待允许任务继续执行,并对操作状态进行查询或设置回调函数。
  • 具体区别如下:
    • 阻塞等待会造成任务阻塞,无法进行其他操作,而非阻塞等待允许任务继续执行其他操作。
    • 阻塞等待的操作结果通常是通过阻塞等待的方式获取,而非阻塞等待需要主动轮询或回调来获取操作结果。
    • 阻塞等待的效率较低,因为任务可能需要等待较长时间才能继续执行,而非阻塞等待可以提高任务的响应速度和并发性。
    • 阻塞等待通常使用在同步模式下,保证任务的执行顺序;非阻塞等待则常用于异步模式下,充分利用系统资源。

进程的阻塞等待方式

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
pid_t pid;
pid = fork();
if(pid < 0){
printf("%s fork error\n",__FUNCTION__);
return 1;
} else if( pid == 0 ){ //child
printf("child is run, pid is : %d\n",getpid());
sleep(5);
exit(257);
} else{
int status = 0;
pid_t ret = waitpid(-1, &status, 0);//阻塞式等待,等待5S
printf("this is test for wait\n");
if( WIFEXITED(status) && ret == pid ){
printf("wait child 5s success, child return code is :%d.\n",WEXITSTATUS(status));
}else{
printf("wait child failed, return.\n");
return 1;
}
}
return 0;
}

 进程的非阻塞等待方式

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
pid_t pid;
pid = fork();
if(pid < 0){
printf("%s fork error\n",__FUNCTION__);
return 1;
}else if( pid == 0 ){ //child
printf("child is run, pid is : %d\n",getpid());
sleep(5);
exit(1);
} else{
int status = 0;
pid_t ret = 0;
do
{
ret = waitpid(-1, &status, WNOHANG);//非阻塞式等待
if( ret == 0 ){
printf("child is running\n");
}
sleep(1);
}while(ret == 0);
if( WIFEXITED(status) && ret == pid ){
printf("wait child 5s success, child return code is :%d.\n",WEXITSTATUS(status));
}else{
printf("wait child failed, return.\n");
return 1;
}
}
return 0;
}

后记

本篇讲述了进程等待的相关知识。

相关文章:

《Linux从练气到飞升》No.19 进程等待

&#x1f57a;作者&#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux菜鸟刷题集 &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f3c7;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64c;收藏❤️关注对我真的…...

OpenCV

文章目录 OpenCV学习报告读取图片和网络摄像头1.1 图片读取1.2 视频读取1.1.1 读取视频文件1.1.2读取网络摄像头 OpenCV基础功能调整、裁剪图像3.1 调整图像大小3.2 裁剪图像 图像上绘制形状和文本4.1 图像上绘制形状4.2图像上写文字 透视变换图像拼接颜色检测轮廓检测人脸检测…...

hadoop解决数据倾斜的方法

分析&回答 1&#xff0c;如果预聚合不影响最终结果&#xff0c;可以使用conbine&#xff0c;提前对数据聚合&#xff0c;减少数据量。使用combinner合并,combinner是在map阶段,reduce之前的一个中间阶段,在这个阶段可以选择性的把大量的相同key数据先进行一个合并,可以看做…...

打造坚不可摧的代码堡垒 - 搭建GitLab私有仓库完全指南

在现代软件开发中&#xff0c;版本控制是一个不可或缺的环节。GitLab是一个流行的版本控制平台&#xff0c;允许开发团队协同工作并管理他们的代码。在某些情况下&#xff0c;您可能希望将您的代码托管在一个私有仓库中&#xff0c;以确保代码的安全性和机密性。在本文中&#…...

linux把文件压缩/解压成.tar.gz/tar/tgz等格式的命令大全

linux把文件压缩/解压成.tar.gz/tar/tgz等格式的命令大全 linux压缩命令常用的有&#xff1a;tar&#xff0c;tgz&#xff0c;gzip&#xff0c;zip&#xff0c;rar 一&#xff0c;tar&#xff08;一&#xff09; tar压缩命令#说明&#xff1a;#举例&#xff1a; &#xff08;二…...

用户角色权限demo后续出现问题和解决

将demo账号给到理解和蒋老师&#xff0c;测试的时候将登录人账号改了&#xff0c;结果登录不了了&#xff0c;后续还需要分配权限无法更改他人的账号和密码 将用户和权限重新分配&#xff08;数据库更改&#xff0c;不要学我&#xff09; 试着登录还是报一样的错&#xff0c;但…...

SpringBoot在IDEA里实现热部署

使用步骤 1.引入依赖 <!--devtools热部署--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional><scope>true</scope><versi…...

浅谈Linux中的mkdir -p

mkdir 是一个用于创建目录&#xff08;目录树&#xff09;的 Unix 和 Linux 命令。-p 选项允许创建一个目录和它不存在的父目录。换句话说&#xff0c;-p 选项确保了指定的整个目录路径都会被创建。 基础用法 如果你只是运行 mkdir new_directory&#xff0c;这个命令会尝试在…...

设计模式—职责链模式(Chain of Responsibility)

目录 思维导图 什么是职责链模式&#xff1f; 有什么优点呢&#xff1f; 有什么缺点呢&#xff1f; 什么场景使用呢&#xff1f; 代码展示 ①、职责链模式 ②、加薪代码重构 思维导图 什么是职责链模式&#xff1f; 使多个对象都有机会处理请求&#xff0c;从而避免请…...

vue小测试之拖拽、自定义事件

在开始之前我去复习了一下&#xff0c;clientX、clientY、pageX、pageY的区别&#xff0c;对于不熟悉offsetLeft和offsetTop的也可以在这里去复习一下。 vue拖拽指令之offsetX、clientX、pageX、screenX_wade3po的博客-CSDN博客_vue offset 客户区坐标位置&#xff08;clientX&…...

时序预测 | MATLAB实现DBN-SVM深度置信网络结合支持向量机时间序列预测(多指标评价)

时序预测 | MATLAB实现DBN-SVM深度置信网络结合支持向量机时间序列预测(多指标评价) 目录 时序预测 | MATLAB实现DBN-SVM深度置信网络结合支持向量机时间序列预测(多指标评价)效果一览基本描述程序设计参考资料 效果一览 基本描述 MATLAB实现DBN-SVM深度置信网络结合支持向量机…...

Python中异步编程是什么意思? - 易智编译EaseEditing

异步编程是一种编程模式&#xff0c;用于处理可能会导致程序等待的操作&#xff0c;例如网络请求、文件读写或长时间的计算任务&#xff0c;而不会阻塞整个程序的执行。 在传统的同步编程中&#xff0c;当程序执行一个耗时的操作时&#xff0c;它会等待该操作完成&#xff0c;…...

【JS真好玩】自动打字机效果

目录 一、前言二、布局分析三、总体样式四、中间部分五、底部5.1 div5.2 label5.3 input 六、JS让它动起来6.1定时器6.2 字符串处理6.2.1 slice6.2.2 splice6.3.3 split 七、总结 一、前言 大家好&#xff0c;今天实现一个自动打字机效果&#xff0c;旨在实现一些网上很小的de…...

宠物赛道,用AI定制宠物头像搞钱项目教程

今天给大家介绍一个非常有趣&#xff0c;而粉丝价值又极高&#xff0c;用AI去定制宠物头像或合照的AI项目。 接触过宠物行业应该知道&#xff0c;获取1位铲屎官到私域&#xff0c;这类用户的价值是极高的&#xff0c;一个宠物粉&#xff0c;是连铲个屎都要花钱的&#xff0c;每…...

基于vue和element的脚手架【vue-element-admin 和vue-element-plus-admin 】

vue-element-admin vue-element-admin 是一个后台前端解决方案&#xff0c;它基于 vue 和 element-ui实现 介绍 | vue-element-adminA magical vue adminhttps://panjiachen.github.io/vue-element-admin-site/zh/guide/ vue-element-plus-admin vue-element-plus-admin 是一…...

推荐Java开发常用的工具类库google guava

Guava Guava是一个Google开源的Java核心库&#xff0c;它提供了许多实用的工具和辅助类&#xff0c;使Java开发更加简洁、高效、可靠。目前和hutool一起&#xff0c;是业界常用的工具类库。shigen也比较喜欢使用&#xff0c;在这里列举一下常用的工具类库和使用的案例。 参考…...

stencilJs学习之构建 Drawer 组件

前言 在之前的学习中&#xff0c;我们已经掌握了 stencilJs 中的一些核心概念和基础知识&#xff0c;如装饰器 Prop、State、Event、Listen、Method、Component 以及生命周期方法。这些知识是构建复杂组件和应用的基础&#xff0c;而抽屉组件是一个很好的示例&#xff0c;能够…...

hbase 国内镜像 极速下载

文章目录 国内镜像汇总-极速下载【JavaPub版】 lucene国内镜像 https://mirrors.cloud.tencent.com/apache/hbase/ 国内镜像汇总-极速下载【JavaPub版】...

Linux驱动——Tiny4412芯片_Source Insight的下载+Linux3.5内核下工程的创建

文章目录 前言Source Insight的下载1.下载地址2.下载步骤 linux3.5内核下工程的创建 前言 本博客仅作为笔记总结&#xff0c;以及帮助有需要的人&#xff0c;不作权威解释。 Source Insight的下载 1.下载地址 官网&#xff1a;https://www.sourceinsight.com/ 另外可以选择…...

rust交叉编译 在mac下编译linux和windows

系统版本macbook proVentura 13.5linux ubuntu22.04.3 LTS/18.04.6 LTSwindowswindows 10 专业版 20H2mac下rustc --versionrustc 1.74.0-nightly (58eefc33a 2023-08-24)查看当前系统支持的交叉编译指定系统版本列表 rustup target list如果已经安装这里会显示(installed)。…...

linux离线环境安装redis

先检查gcc版本&#xff0c;使用gcc --version进行检查&#xff0c;版本在5以下的&#xff0c;安装redis要安装redis6以下的版本 如果没有gcc命令&#xff0c;要先安装gcc命令。因为是离线环境&#xff0c;yum命令什么的用不了。为了安装gcc&#xff0c;进行了几种尝试。 1、下…...

记录学习--字节码解析try catch

1.示例代码 Testpublic void someTest() {String s "111";try {s "222";int i 1/0;} catch (Exception e){e.printStackTrace();System.out.println(s);}System.out.println(s);}2.示例代码对应的字节码 0 ldc #2 <111>2 astore_13 ldc #3 <22…...

多线程与高并发——并发编程(3)

文章目录 三、锁1 锁的分类1.1 可重入锁、不可重入锁1.2 乐观锁、悲观锁1.3 公平锁、非公平锁1.4 互斥锁、共享锁2 深入synchronized2.1 类锁、对象锁2.2 synchronized的优化2.3 synchronized实现原理2.4 synchronized的锁升级2.5 重量级锁底层 ObjectMonitor3 深入ReentrantLo…...

关于内网主备+https

先openssl证书 整体流程为&#xff1a; 1、页面访问为https&#xff0c;在电脑修改hosts文件&#xff0c;如域名为 babaozhou.com, 则配置为 ip1 babaozhou.com&#xff0c;ip2 babaozhou.com&#xff1b; 也就是说同域名关联两个ip&#xff0c;这样如果服务器1ping不通了则可…...

java入坑之网络编程

一、 网络基础知识 1.1网卡 1.2IP地址 1.3端口 1.4保留IP 1.5网络协议 二、UDP 编程 2.1相关概念 计算机通讯&#xff1a;数据从一个IP的port出发&#xff08;发送方&#xff09;&#xff0c;运输到另外一个IP的port&#xff08;接收方&#xff09; UDP&#xff1a;无连接无…...

A Survey on Large Language Model based Autonomous Agents

本文是LLM系列的文章&#xff0c;针对《A Survey on Large Language Model based Autonomous Agents》的翻译。 基于大模型的自动agents综述 摘要1 引言2 基于LLM的自动代理构建3 基于LLM的自动代理应用4 基于LLM的自动代理评估5 相关综述6 挑战6.1 角色扮演能力6.2 广义与人对…...

Integer、Long 等包装类 == 值判断、地址判断与缓存

先看下以下代码和输出 public static void main(String[] args) throws Exception{Integer a-128;Integer aa-128;System.out.printf("aaa? %s \n",aaa);Integer b127;Integer bb127;System.out.printf("bbb? %s \n",bbb);Integer c128;Integer cc128;Sy…...

numpy学习:reshape和resize

.reshape 与 .resize reshape&#xff1a;有返回值&#xff0c;所谓有返回值&#xff0c;即不对原始多维数组进行修改&#xff1b; resize&#xff1a;无返回值&#xff0c;所谓无返回值&#xff0c;即会对原始多维数组进行修改&#xff1b;...

JPA在不写sql的情况下实现模糊查询

本文已收录于专栏 《Java》 目录 背景介绍概念说明单字段模糊匹配&#xff1a;多字段模糊匹配&#xff1a; 实现过程代码实现1.写一个实体类去实现Specification接口&#xff0c;重写toPredicate方法2.定义一个接口去继承JpaRepository接口&#xff0c;并指定返回的类型和参数类…...

Java设计模式之单例模式

单例模式是一种设计模式&#xff0c;它的目的是确保一个类只有一个实例&#xff0c;并提供一个全局访问点来访问该实例。这个模式通常在需要控制资源访问权、限制实例化数量或实现全局共享时使用。 在实现单例模式时&#xff0c;一般会定义一个私有的构造函数&#xff0c;以防…...