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

Linux进程等待

在这里插入图片描述

文章目录

    • 1. 为什么要进程等待
    • 2. 进程等待的方法
      • wait
      • waitpid
      • 非阻塞轮询

1. 为什么要进程等待

  • 子进程退出,如果父进程还未结束,没有管这个子进程,那么就可能会造成“僵尸进程”问题,进而出现内存泄漏

  • 如果这个进程变成了“僵尸进程”,那么就无法被杀死,因为“无法杀死死去的进程”,kill -9也不行

    #include<stdio.h>
    #include<unistd.h>
    #include<stdlib.h>int main()
    {pid_t id = fork();if(id<0){//创建子进程失败perror("fork fail");return 0;}else if(id == 0){int cnt = 5;while(cnt--){printf("I am child %d ,ppid:%d\n",getpid(),getppid());sleep(1);}exit(0);}else{while(1){printf("I am parent %d ,ppid:%d\n",getpid(),getppid());sleep(1);}}return 0;
    }
    

    z_proc

  • 父进程派给子进程的任务,我们需要知道执行情况。例如结果是否正确,是否正常退出等

  • 父进程通过等待的方式,将子进程回收,获取子进程的退出信息

2. 进程等待的方法

wait

#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*status);
返回值:成功返回被等待进程pid,失败返回-1。
参数:输出型参数,获取子进程退出状态,不关心则可以设置成为NULL
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
void runChild()
{int cnt = 5;while(cnt--){printf("I am a child:%d, ppid:%d  cnt:%d\n",getpid(),getppid(),cnt);sleep(1);}
}int main()
{for(int i=0;i<5;i++){pid_t id = fork();if(id == 0){runChild();exit(0);}printf("I am parent:%d\n",getpid());}                                                                                   sleep(6);//进程回收for(int i=0;i<5;i++){pid_t ret = wait(NULL);if(id>0){printf("wait %d success\n",ret);}}sleep(3);return 0;
}

wait

如果我们的子进程一直不退出,那么父进程在默认wait的时候,就不返回,一直等待,这种默认叫做进程阻塞状态

另外,等待的进程,一定要是自己的子进程!如果这个是别人的,等不到结果的,最后只会返回wait failed,人生也如此。

waitpid

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一样,这其实是一个输出型参数

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{pid_t id = fork();if(id<0){perror("fork error");return 1;}else if(id == 0){int cnt = 5;while(cnt--){printf("I am child,pid:%d,ppid:%d,cnt:%d\n",getpid(),getppid(),cnt);sleep(1);}exit(11);}else{int cnt = 8;while(cnt--){printf("I am parent,pid:%d,ppid:%d,cnt:%d\n",getpid(),getppid(),cnt);sleep(1);}int status = 0;pid_t ret = waitpid(id,&status,0);if(id == ret){printf("wait success,ret:%d status:%d\n",ret,status);}sleep(3);}return 0;
}

运行结果:
image-20231029202921435

这个个status它的类型虽然是int但它其实被当作了几部分来使用的

因为子进程退出之后父进程要收到几种信息:

  1. 子进程代码是否异常
  2. 退出结果是否正确,如果不对,是为什么不对,不同的退出码,表示不同的出错原因

所以,status是被拆作了几部分image-20231029202159311

kill -l指令检查发现,并没有0号信号,这就是因为当低七位全为0的时候,表示进程无异常,正常退出

image-20231029202340995

所以我们需要提取这个status的结果

int status = 0;
pid_t ret = waitpid(id,&status,0);
if(id == ret)
{//7f 0111111printf("wait success,ret:%d exit sig:%d,exit code:%d\n",ret,status&0x7f,(status>>8)&0xff);
}

image-20231029203347326

当然了,这里不必要我们手动自己提取,系统为我们提供了宏:

  • WIFEXITED(status):检测进程退出时是否异常
  • WEXITSTATUS(status):提前退出码

有了这些,那么就能写出完整的多进程代码了。

这里为什么要调用wait等系统调用呢?为什么不直接用全局变量来接收子进程的这些信息呢?

这是因为**进程具有独立性!!!**所以必须通过系统调用,让操作系统去拿这些数据。

非阻塞轮询

这里waitpid的第三个参数,我们上面都说设置的0,这表示默认进行等待,如果子进程一直不退出,那么父进程就一直在那等着,进入阻塞状态。

如果不想一直等待,那么可以采用非阻塞轮询pid_ t waitpid(pid_t pid, int *status, int options)第三个参数options采用宏WNOHANG

什么叫做非阻塞轮询?

就好比和别人约好了出来吃饭,中午12:00到xx地方碰面,你自己先到了,然后就一直在那等着,什么也不干,这就叫做默认等待,也就是我们的阻塞;

第二次出来玩,你又先到了,你吸取了上次的经验,你到了之后,直接打电话问对方,还有多久?对方答复10分钟,那么对方还未到的时候,你可以去别处逛逛,逛的差不多了,又打电话,还多久?对方答复5分钟,好,那么你提前去点餐,对方到了,基本上菜也就上了。这个过程,就是非阻塞轮询

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{pid_t id = fork();if(id < 0){perror("fork error");return 1;}else if(id == 0){int cnt = 2;while(cnt--){printf("I am child,pid:%d,ppid:%d,cnt:%d\n",getpid(),getppid(),cnt);sleep(1);}exit(27);}else{int status = 0;while(1){pid_t ret = waitpid(id,&status,WNOHANG);if(ret>0) //等待成功{if(WIFEXITED(status)){printf("进程正常退出,退出码为:%d\n",WIFEXITED(status));}else{printf("进程异常\n");}break;}else if(ret<0)  //等待失败{printf("wait failed\n");break;}else{printf("我在轮询\n");//sleep(1);}}}return 0;
}

这里可以看出,当子进程未退出的时候,父进程一直在轮询,知道子进程退出为止

WNOHANG

这个期间,父进程就能够做自己的事情了,下面来模拟这个过程

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
#define TASK_NUM 5
typedef void(*task_t)();
task_t tasks[TASK_NUM];//模拟任务
void task1()
{printf("执行任务1,pid:%d\n",getpid());
}void task2()
{printf("执行任务2,pid:%d\n",getpid());
}void task3()
{printf("执行任务3,pid:%d\n",getpid());
}//任务管理
int addTask(task_t t);
void initTask()
{for(int i=0;i<TASK_NUM;i++) tasks[i] = NULL;addTask(task1);addTask(task2);addTask(task3);
}int addTask(task_t t)
{int pos = 0;for(;pos<TASK_NUM;pos++){if(!tasks[pos]) break;}if(pos == TASK_NUM) return -1;tasks[pos] = t;return 0;
}void excuteTask()
{for(int i=0;i<TASK_NUM;i++){if(!tasks[i]) continue;tasks[i]();}
}int main()
{pid_t id = fork();if(id < 0){perror("fork error");return 1;}else if(id == 0){int cnt = 5;while(cnt--){printf("I am child,pid:%d,ppid:%d,cnt:%d\n",getpid(),getppid(),cnt);sleep(1);}exit(27);}else{initTask();int status = 0;while(1){pid_t ret = waitpid(id,&status,WNOHANG);if(ret>0) //等待成功{if(WIFEXITED(status)){printf("进程正常退出,退出码为:%d\n",WIFEXITED(status));}else{printf("进程异常\n");}break;}else if(ret<0)  //等待失败{printf("wait failed\n");break;}else{excuteTask();//printf("我在轮询\n");usleep(500000);}}}return 0;
}

waitpoll

在轮询过程中的任务,尽量是比较轻量化的工作,因为这是在等待的过程,不要做太过于复杂的事情

另外,子进程也不一定非得是结束之后立马回收,要的是及时回收

在这些过程当中,进程谁先执行是由调度器决定,我们并不清楚,但是父进程一定是最后退出的进程,所以有了进程等待,我们就能够保证,父进程是最后退出的。

相关文章:

Linux进程等待

文章目录 1. 为什么要进程等待2. 进程等待的方法waitwaitpid非阻塞轮询 1. 为什么要进程等待 子进程退出&#xff0c;如果父进程还未结束&#xff0c;没有管这个子进程&#xff0c;那么就可能会造成“僵尸进程”问题&#xff0c;进而出现内存泄漏 如果这个进程变成了“僵尸进程…...

python设计模式笔记1:创建型模式 工厂模式和抽象工厂模式

1.工厂模式 (1) 导入所需的模块&#xff08; json 和 ElementTree &#xff09;。 (2) 定义 JSON数据提取器类&#xff08; JSONDataExtractor &#xff09;。 (3) 定义 XML数据提取器类&#xff08; XMLDataExtractor &#xff09;。 (4) 添加工厂函数 dataextraction_factor…...

第五章 I/O管理 一、I/O设备的基本概念和分类

目录 一、什么是I/O设备 1、定义&#xff1a; 2、按特性分类&#xff1a; 3、按传输速率分类&#xff1a; 4、按信息交换的方式分类&#xff1a; 二、总结 一、什么是I/O设备 1、定义&#xff1a; I/O设备就是可以将数据输入到计算机&#xff0c;或者可以接收计算机输出…...

vue3动态引入图片(:src)

vite 官方默认的配置&#xff0c;如果资源文件在assets文件夹打包后会把图片名加上 hash值&#xff0c;但是直接通过 :src"imgSrc"方式引入并不会在打包的时候解析&#xff0c;导致开发环境可以正常引入&#xff0c;打包后却不能显示的问题 实际上我们不希望资源文…...

Android-登录注册页面(第三次作业)

第三次作业 - 登录注册页面 题目要求 嵌套布局。使用线性布局的嵌套结构&#xff0c;实现登录注册的页面。&#xff08;例4-3&#xff09; 创建空的Activity 项目结构树如下图所示&#xff1a; 注意&#xff1a;MainActivity.java文件并为有任何操作&#xff0c;主要功能集中…...

[论文精读]How Powerful are Graph Neural Networks?

论文原文&#xff1a;[1810.00826] How Powerful are Graph Neural Networks? (arxiv.org) 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#x…...

Redis实现分布式锁之----超时和失效(非原子性)问题----解决方案

Redis实现分布式锁之----超时和失效&#xff08;非原子性&#xff09;问题----解决方案 超时和失效&#xff08;非原子性&#xff09;问题 原子性问题&#xff1a;上锁时存入线程名称&#xff0c;删除时要先判断锁内的名称是不是自己的&#xff0c;是再删除&#xff0c;但是后…...

Android使用Hilt依赖注入,让人看不懂你代码

前言 之前接手的一个项目里有些代码看得云里雾里的&#xff0c;找了半天没有找到对象创建的地方&#xff0c;后来才发现原来使用了Hilt进行了依赖注入。Hilt相比Dagger虽然已经比较简洁&#xff0c;但对初学者来说还是有些门槛&#xff0c;并且网上的许多文章都是搬自官网&…...

ZYNQ连载01-ZYNQ介绍

ZYNQ连载01-ZYNQ介绍 1. ZYNQ 参考文档&#xff1a;《ug585-zynq-7000-trm.pdf》 ZYNQ分为PS和PL两大部分&#xff0c;PS即ARM&#xff0c;PL即FPGA&#xff0c;PL作为PS的外设。 2. 方案 ZYNQ7020为双核A9架构&#xff0c;多核处理器常用的运行模式为AMP(非对称多处理)和…...

第十节——Vue组件

一、什么是组件 组件(Component)是vue.js中很强大的一个功能&#xff0c;可以将一些可重用的代码进行封重用。 所有的Vue 组件同时也是Vue 的实例&#xff0c;可以接受使用相同的选项对象和提供相同的生命周期钩子。 一句话概括&#xff1a;组件就是可以扩展HTML元素&#xff…...

Redis(01)| 数据结构

这里写自定义目录标题 Redis 速度快的原因除了它是内存数据库&#xff0c;使得所有的操作都在内存上进行之外&#xff0c;还有一个重要因素&#xff0c;它实现的数据结构&#xff0c;使得我们对数据进行增删查改操作时&#xff0c;Redis 能高效的处理。 因此&#xff0c;这次我…...

SpringBoot工程启动时自动创建数据库、数据表

文章目录 一&#xff0c;序二&#xff0c;自动创建数据库1. 数据源配置2. 修改支持数据库创建 三&#xff0c;自动创建数据库表以及数据1. 准备DDL、DML语句1.&#xff09;典型DDL语句2.&#xff09;典型DML语句 2. 设置初始化参数 四、源码传送 一&#xff0c;序 针对Java工程…...

Uni-app智慧工地可视化信息平台源码

智慧工地的核心是数字化&#xff0c;它通过传感器、监控设备、智能终端等技术手段&#xff0c;实现对工地各个环节的实时数据采集和传输&#xff0c;如环境温度、湿度、噪音等数据信息&#xff0c;将数据汇集到云端进行处理和分析&#xff0c;生成各种报表、图表和预警信息&…...

计算机网络重点概念整理-第五章 传输层【期末复习|考研复习】

第五章 传输层 【期末复习|考研复习】 计算机网络系列文章传送门&#xff1a; 第一章 计算机网络概述 第二章 物理层 第三章 数据链路层 第四章 网络层 第五章 传输层 第六章 应用层 第七章 网络安全 计算机网络整理-简称&缩写 文章目录 第五章 传输层 【期末复习|考研复习…...

Java毕业设计 SpringBoot 新能源充电桩管理系统

Java毕业设计 SpringBoot 新能源充电桩管理系统 SpringBoot 新能源充电桩管理系统 功能介绍 管理员 登录 验证码 注册 系统用户管理 普通用户管理 通知公告管理 留言管理 充电站管理 充电桩管理 充电桩预约 充电管理 订单管理 修改密码 普通用户 登录 修改个人资料 通知公告…...

JNI接口

NewStringUTF和NewString接口测试 打开输入法“显示表情与符号” 右键&#xff0c;拷贝字符简介 &#x1f34c; 香蕉 Unicode: U1F34C&#xff0c;UTF-8: F0 9F 8D 8C unicode码 &#x1f34c; \U0001f34c utf-8编码为0xf09f8d8c&#xff0c;结合char或char8_t的长度&…...

国内好用的免费ai软件

国内就只推荐几个大厂&#xff0c;基本感受都差不多 百度文心一言 百度研发的知识增强大语言模型&#xff0c;能够与人对话互动&#xff0c;回答问题&#xff0c;协助创作&#xff0c;高效便捷地帮助人们获取信息、知识和灵感。 地址&#xff1a;文心一言 (baidu.com) 阿里通…...

MAC缓解WebUI提示词反推

当前环境信息&#xff1a; 在mac上安装好stable diffusion后&#xff0c;能做图片生成了之后&#xff0c;遇到一些图片需要做提示词反推&#xff0c;这个时候需要下载一个插件&#xff0c;参考&#xff1a; https://gitcode.net/ranting8323/stable-diffusion-webui-wd14-tagg…...

【设计模式之原型模式 】– C++

5. 原型模式 – 复制粘贴&#xff0c;一摸一样 简介 原型模式&#xff08;Prototype Pattern&#xff09;是一种创建型设计模式&#xff0c;其主要目的是通过复制现有对象来创建新的对象&#xff0c;而无需从头开始构建。 使用场景 它通常用于以下情况&#xff1a; 当一个系统…...

Flask路由机制分析之二

一、前言 上篇 《Flask 路由机制分析之一》主要讲了Python函数的特性以及装饰器的基本概念&#xff0c;这节我们具体分析一下路由内部机制&#xff0c;Flask路由依赖于werkzegu的routing模块来实现。 二、werkzegu的routing模块介绍 Werkzegu库的routing模块主要功能在于URL…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战

前言 现在我们有个如下的需求&#xff0c;设计一个邮件发奖的小系统&#xff0c; 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其…...

【kafka】Golang实现分布式Masscan任务调度系统

要求&#xff1a; 输出两个程序&#xff0c;一个命令行程序&#xff08;命令行参数用flag&#xff09;和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽&#xff0c;然后将消息推送到kafka里面。 服务端程序&#xff1a; 从kafka消费者接收…...

2024年赣州旅游投资集团社会招聘笔试真

2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

基于Uniapp开发HarmonyOS 5.0旅游应用技术实践

一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架&#xff0c;支持"一次开发&#xff0c;多端部署"&#xff0c;可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务&#xff0c;为旅游应用带来&#xf…...

页面渲染流程与性能优化

页面渲染流程与性能优化详解&#xff08;完整版&#xff09; 一、现代浏览器渲染流程&#xff08;详细说明&#xff09; 1. 构建DOM树 浏览器接收到HTML文档后&#xff0c;会逐步解析并构建DOM&#xff08;Document Object Model&#xff09;树。具体过程如下&#xff1a; (…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”

2025年#高考 将在近日拉开帷幕&#xff0c;#AI 监考一度冲上热搜。当AI深度融入高考&#xff0c;#时间同步 不再是辅助功能&#xff0c;而是决定AI监考系统成败的“生命线”。 AI亮相2025高考&#xff0c;40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕&#xff0c;江西、…...

ABAP设计模式之---“简单设计原则(Simple Design)”

“Simple Design”&#xff08;简单设计&#xff09;是软件开发中的一个重要理念&#xff0c;倡导以最简单的方式实现软件功能&#xff0c;以确保代码清晰易懂、易维护&#xff0c;并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计&#xff0c;遵循“让事情保…...

Spring是如何解决Bean的循环依赖:三级缓存机制

1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间‌互相持有对方引用‌,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...

IP如何挑?2025年海外专线IP如何购买?

你花了时间和预算买了IP&#xff0c;结果IP质量不佳&#xff0c;项目效率低下不说&#xff0c;还可能带来莫名的网络问题&#xff0c;是不是太闹心了&#xff1f;尤其是在面对海外专线IP时&#xff0c;到底怎么才能买到适合自己的呢&#xff1f;所以&#xff0c;挑IP绝对是个技…...