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

非阻塞轮询

目录

  • 前言
  • 1.options 参数
  • 2. 非阻塞轮询
  • 3. 模拟非阻塞轮询
  • 4. 非阻塞轮询 + 执行其它任务

前言

继上一篇文章 详谈进程等待 讲到 waitpid 系统调用,在该系统调用接口中还有一个 options 参数,本篇文章介绍 watipid 系统调用中的options 参数 以及 什么是非阻塞轮询,非阻塞轮询的同时是如何执行其它任务的。

1.options 参数

pid_t waitpid(pid_t pid, int *status, int options);  

当父进程 waitpid 等待子进程时,如果子进程退出了,那么父进程读取其 PCB 中的信号码和退出码返回即可;如果子进程迟迟不退出,那父进程就得一直等下去,进而导致父进程阻塞调用,也即父进程处于阻塞状态。

而 options 选项就是设置父进程等待的方式:

  • 默认0,阻塞等待方式(子进程处于 R 状态,操作系统把父进程状态由 R -> S,然后把父进程投递到子进程 PCB 中维护的等待队列,当子进程退出了,操作系统再将父进程唤醒。换言之,也即父进程一直等待子进程的本质就是阻塞与子进程的等待队列中)
  • 非阻塞轮询 WNOHANG。

2. 非阻塞轮询

将 options 设置为 WNOHANG,就是非阻塞轮询,又即等待的时候不要夯住(调用时系统不返回,进程阻塞,系统无响应等,就称为该系统 或 该接口 被夯住了,也即阻塞的专业词语)。

所以什么是非阻塞呢??

故事线:outlier 大学即将迎来一学期一度的期末考试,你作为学渣,平时不上课,课后不作为,马上要考C语言了,你慌的一批。于是马上打电话给好朋友张三(张三是一名学霸,课后笔记大师):“张三啊,马上要考试了,带上你的笔记过来辅导一下你爹”。张三:“儿子,你等会,我在复习,过一会就好了”,说完就挂掉电话。于是你也就等下去了,等了十来分钟,你又打电话问张三好了没,张三回复了再等会。于是你又再等下去。十分钟后,再打一次。。。。二十分钟,再打一次,这一次张三说,我看到你了,你今天穿白T 对吧,你二话不说,啪的一下挂掉了电话。因为你穿的是蓝T。循环往复,最终你们终于踏上去图书馆的道路。

上述的张三就是 操作系统;你就是 用户;打电话的过程就是调用系统调用;打电话的本质就是检查操作系统(张三)的状态;每次打电话得到回复之后立马又挂掉就是 系统调用立马返回;每一次打电话检测时操作系统(张三)没好,用户(你)不会一直占线等待,而是立马返回,这叫做非阻塞!而一直打电话问张三,就是轮询!

非阻塞轮询 = 非阻塞 + 循环

下学期,outlier再次迎来数据结构期末考试,你依旧找到张三,“儿子啊,老地方啊”。张三还是让你等着,他还没准备好。但这次,你跟张三说:“上次一直打你电话,我也不知道你啥时候能好,这次我就不挂了,你好了,下楼见到我了,再挂吧”

而这种占线等待回复,就叫做 阻塞调用!操作系统一直在执行着某种任何,用户一直在检查操作系统任务的完成情况,检测时,即便操作系统任务没完成,系统调用也不返回,即阻塞调用。

下学年,操作系统考试如期而至。你也听闻操作系统的难处,因此你不敢怠慢,吸取前两次的经历,你觉得不能干等着浪费时间。
因此当张三还没下楼找你时,你就在自己宿舍看书复习,时不时的在打电话问一下张三好了没。

这一次,你学聪明了,一边等待张三,一边做着自己的事情,这就是非阻塞轮询的同时,可以执行其它任务。而纯阻塞调用,是无法执行其它任务的,只能干等着。非阻塞轮询 + 执行其它任务,才是最常见的。

所以 waitpid 返回值 ret > 0 时,即代表等待成功; ret < 0 即等待失败;而大量存在的情况是 ret = 0 的情况,即非阻塞轮询检查状态时,目标子进程还没有就绪(即没有退出),而子进程没有退出,并不代表调用的失败或成功,只不过是等待的条件还没有就绪而已!


3. 模拟非阻塞轮询

int main()
{pid_t id = fork();if(id < 0){perror("fork");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);cnt--;sleep(1);}exit(1);}else {int status = 0;while(1)	// 轮询{pid_t ret = waitpid(id, &status, WNOHANG);  // 非阻塞if(ret > 0){if(WIFEXITED(status)) printf("process is normal, exit_code: %d\n", WEXITSTATUS(status));else printf("the process terminated abnormally! \n");break;}else if(ret == 0){printf("the child process has not exited and continues to poll non-blocking\n");sleep(1);}else {
0               printf("wait failed!\n");break;}}sleep(1);}return 0;
}

在这里插入图片描述


4. 非阻塞轮询 + 执行其它任务

当 waitpid 等待子进程时,检测到子进程状态还没退出,于是立马返回,这之后是如何执行自己的其它任务的呢??执行任务的同时有没有可能错过子进程退出呢??

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define TASK_NUM 10typedef void(*task_t)();    // 函数指针
task_t tasks[TASK_NUM];     // 函数指针数组void task1() {  printf("task 1, pid: %d\n", getpid()); }
void task2() {  printf("task 2, pid: %d\n", getpid()); }
void task3() {  printf("task 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;  // 任务队列满,返回-1tasks[pos] = t;   // 添加指定任务的函数指针return 0;
}void DelTask() { }
void CheckTask() { }
void UpdateTask() { }void ExecuteTask()		// 执行任务
{for(int i = 0; i < TASK_NUM; i++){if(!tasks[i]) continue;tasks[i]();     // 回调函数}
}int main()
{InitTask();pid_t id = fork();if(id < 0){perror("fork");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);cnt--;sleep(1);}exit(1);}else {int status = 0;while(1){pid_t ret = waitpid(id, &status, WNOHANG);  // 非阻塞if(ret > 0){if(WIFEXITED(status)) {printf("process is normal, exit_code: %d\n", WEXITSTATUS(status));}else {printf("the process terminated abnormally! \n");}break;}else if(ret == 0){ExecuteTask(); usleep(500000);}else {printf("wait failed!\n");break;}}}return 0;
}

在这里插入图片描述

以上就是创建单进程的模拟非阻塞轮询的同时,执行自己的其它任务,如果是创建多进程,只需要将 waitpid 中的 pid 参数设置为 -1(等待任意一个子进程),在等待成功之后,不是立马 break,而是维护一个计数器,等待成功一个子进程,就让计数器 -1,直到 计数器 == 0 然后 break 即可。

而至于会不会有可能父进程在执行其它任务的时候,错过了子进程这个问题,所谓错过,无非就是子进程退出的时候,父进程不会立刻马上立刻对其进行回收,但是当父进程执行完自己的任务回来之后,也会对其进行回收。而一般情况下,非阻塞轮询进行等待子进程,才是父进程此时的主线任务,执行其它任务,只不过是顺手的事,不让父进程干等着罢了,因此这类任务一般都是很轻量级的,所以一般也不必担心会不会执行了其它任务,子进程就没有人回收的问题。并且当子进程多一点时,子进程退出时,父进程晚一点来回收,反而可以集中回收,提高效率。

而至于创建出来的诸多子进程中,哪个进程先被调度,我们并无法知晓。但是,我们必须要知道,最后退出的进程一定是父进程!

为什么?----- 因为所有的子进程是父进程创建出来的,所以父进程有等待所有子进程的职责!父进程需要等待所有子进程退出之后,来回收子进程。这也是进程等待重要的一个原因,在编码得到保障时,父进程最后一个退出,是可以保证释放所有子进程的资源的。


关于非阻塞轮询的内容,小篇介绍到这里,后续文章还会介绍 进程替换

如果感觉该篇文章给你带来了收获,可以 点赞👍 + 收藏⭐️ + 关注➕ 支持一下!

感谢各位观看!

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • C++第四十二弹---C++11新特性深度解析:让你的代码更现代、更高效(中)
  • 【conda】导出和重建 Conda 环境
  • mysql 一主一从数据库的配置文件
  • GPS北斗授时服务器(网络时钟系统)助力金融领域
  • windows权限维持汇总
  • 【ubuntu24.04】AX210/MT9621/USB网络共享访问无线网络
  • 深入解析Node.js中的new URL()构造函数:功能、属性、方法与使用技巧
  • OpenStack——keystone认证服务
  • Linux 流式DMA映射(DMA Streaming Mapping)
  • 【Qt笔记】QToolButton控件详解
  • 有哪些内部知识库类似钉钉,满足企业多样化需求?
  • 服务器(百度云)部署项目(jar包)
  • 基于spring Boot的网上报修平台的设计和实现---附源码94800
  • 深度学习学习经验——全连接神经网络(FCNN)
  • 国内外大模型汇总:Open AI大模型、Google大模型、Microsoft大模型、文心一言大模型、通义千问大模型、字节豆包大模型、智普清言大模型
  • [译] React v16.8: 含有Hooks的版本
  • 《剑指offer》分解让复杂问题更简单
  • 【EOS】Cleos基础
  • 10个确保微服务与容器安全的最佳实践
  • Java知识点总结(JavaIO-打印流)
  • Markdown 语法简单说明
  • Mysql5.6主从复制
  • PHP变量
  • windows下mongoDB的环境配置
  • 从零搭建Koa2 Server
  • 基于Volley网络库实现加载多种网络图片(包括GIF动态图片、圆形图片、普通图片)...
  • 我这样减少了26.5M Java内存!
  • 再次简单明了总结flex布局,一看就懂...
  • 《码出高效》学习笔记与书中错误记录
  • gunicorn工作原理
  • k8s使用glusterfs实现动态持久化存储
  • Nginx惊现漏洞 百万网站面临“拖库”风险
  • ​​​【收录 Hello 算法】9.4 小结
  • ​linux启动进程的方式
  • ​如何使用ArcGIS Pro制作渐变河流效果
  • ​业务双活的数据切换思路设计(下)
  • #微信小程序(布局、渲染层基础知识)
  • #我与Java虚拟机的故事#连载18:JAVA成长之路
  • %3cli%3e连接html页面,html+canvas实现屏幕截取
  • (13)DroneCAN 适配器节点(一)
  • (2024,RWKV-5/6,RNN,矩阵值注意力状态,数据依赖线性插值,LoRA,多语言分词器)Eagle 和 Finch
  • (4)事件处理——(2)在页面加载的时候执行任务(Performing tasks on page load)...
  • (八)Flask之app.route装饰器函数的参数
  • (第二周)效能测试
  • (仿QQ聊天消息列表加载)wp7 listbox 列表项逐一加载的一种实现方式,以及加入渐显动画...
  • (附源码)spring boot北京冬奥会志愿者报名系统 毕业设计 150947
  • (附源码)计算机毕业设计ssm高校《大学语文》课程作业在线管理系统
  • (力扣)循环队列的实现与详解(C语言)
  • (论文阅读30/100)Convolutional Pose Machines
  • (四)软件性能测试
  • (一)VirtualBox安装增强功能
  • (一)基于IDEA的JAVA基础1
  • (转)eclipse内存溢出设置 -Xms212m -Xmx804m -XX:PermSize=250M -XX:MaxPermSize=356m
  • (转)EOS中账户、钱包和密钥的关系
  • (转)树状数组