【进程等待】阻塞等待 | options非阻塞等待

ops/2024/10/19 9:32:17/

目录

waitpid

阻塞等待

options&非阻塞等待 

pid_t返回值 

阻塞等待VS非阻塞等待


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。

阻塞等待

  • 如果子进程没有退出,而父进程在进行执行waitpid进行等待,阻塞等待。
  • 大部分IO类的函数例如scanf各种各样的接口,只要涉及IO的或多或少会可能出现阻塞的状态。
  • 现在所用的大部分接口都是阻塞接口(逻辑简单,容易实现)
  • **阻塞等待(Blocking Wait)**在编程中通常指的是一个线程或进程在等待某个条件满足或某个操作完成之前,会暂停执行其他任务,处于等待状态。这种状态会一直持续,直到等待的条件满足或操作完成,线程或进程才会继续执行后续的任务。在Java中,阻塞等待常用于多线程编程中,用于线程之间的同步和通信。

进程阻塞:

  • 把进程的R状态设置为S状态
  • 把进程的PCB从运行队列移动到等待队列中,不再被调度,而是等待
  • 本质上是等待某种条件发生。
  1. 软件条件满足(子进程退出)
  2. 硬件资源就绪(scanf键盘输入数据发生) 

options&非阻塞等待√

在子进程运行期间,父进程除了等待子进程或者是休眠,能不能干点其他的事情❓

  • 当然可以,在父进程等待,阻塞状态。可以通过设置options来让父进程干点事情。不阻塞等待而是非阻塞等待。

什么又是非阻塞等待呢❓用代码该怎么去实现呢❓ 

  • **非阻塞等待(Non-blocking Wait)**则与阻塞等待相反。当线程或进程在等待某个条件满足或某个操作完成时,它不会暂停执行其他任务,而是会继续执行后续的任务。也就是说,即使等待的条件还没有满足或操作还没有完成,线程或进程也不会被阻塞,而是会继续执行其他的操作。

  • 通过设置options的宏值WNOHANG(wait no hang 等待没有阻塞 = 非阻塞等待)

  • 在计算机中,"HANG" 通常指的是程序或系统出现无响应或停顿的状态,也就是常说的“卡住”或“死机”。当程序或系统由于某种原因(如资源锁定、死循环、死锁或外部系统交互问题等)而无法继续正常执行时,就可能会出现"HANG"的情况。这种情况下,用户可能无法与程序或系统进行交互,需要等待程序或系统恢复正常或进行重启操作。另外,在一些特定的语境下,"HANG" 也可能被用来描述服务器或数据库的某些服务出现故障或无法访问的情况,这也可以被视为一种"宕机"现象。在这种情况下,"HANG" 指的是服务器或数据库的服务因为某种原因而停止响应或无法提供服务。

具体操作

  • options这个参数只要一设置就会出现非阻塞等待。
  • 设置waitpid的WNOHANG本质上是检测一次进程的状态变化。
  • 调用一次waipid就检测一次。每次调用都是检测,多次调用多次检测。
  • 非阻塞等待调用多次waitpid,调用waitpid检测是否退出等待过程无问题,只是子进程还未终止,需要等待下次等待。
  • 综上:非阻塞等待的时候 + 循环 = 非阻塞轮询
 1: myprocess.c1 #include<stdio.h>2 #include<unistd.h>3 #include<string.h>4 #include<stdlib.h>5 #include<sys/types.h>6 #include<sys/wait.h>7 8 void ChildRun()9 {10   int cnt = 5;11   while(cnt--)12   {13     printf("I am child,pid: %d,ppid: %d,cnt: %d\n",getpid(),getppid(),cnt);14     sleep(1);15   }16 }18 int main()19 {20   printf("I am father,pid: %d,ppid: %d\n",getpid(),getppid());//父进程21   pid_t id = fork();22   if(id == 0)//child子进程23   {24     //子进程循环运行25     ChildRun();26     printf("Child quit...\n");27     exit(1);//终止进程,子进程直接僵尸28   }29   //father30   //父进程,父进程在子进程运行期间5ms干点别的事情....31   while(1)32   {33       int status = 0;                                                                                        34       pid_t rid = waitpid(id, &status, WNOHANG);35       if(rid == 0)36       {37         sleep(1);38         printf("child is running,father check next time !\n");39         //DoOtherThing();40       }41       else if(rid > 0)42       {43         if(WIFEXITED(status))44         {45           printf("child quit normal,child exit code: %d\n",WEXITSTATUS(status));46         }47         else48         {49           printf("child quit unnormal!\n");50         }51         printf("wait success,rid: %d\n",rid);52         break;53       }54       else55       {56         printf("wait fail !\n");57         break;58       }                                                                                                      59   }60  //printf("father quit,status: %d,code: %d,signal: %d\n",status,(status>>8)&0XFF,status&0X7F);61 }

解耦☞分析代码逻辑 

【回调函数方式设计一个DoOtherThing在父进程等待的时候实现其他功能】  

【task.c】 

#include "task.h"void PrintLog()
{printf("begin PrintLog...\n");
}
void Download()
{printf("begin Download...\n");
}
void MysqlDataSync()
{printf("begin MySQLDataSync...\n");
}

【task.h】 

#pragma once#include <stdio.h>void PrintLog();
void Download();
void MysqlDataSync();

 【myprocess.c】

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "task.h"typedef void(*func_t)();#define N 3
func_t tasks[N] = {NULL};void LoadTask()
{tasks[0] = PrintLog;tasks[1] = Download;tasks[2] = MysqlDataSync;
}
void HandlerTask()
{for(int i = 0; i < N; i++){tasks[i](); // 回调方式}
}// fahter
void DoOtherThing()
{HandlerTask();
}void ChildRun()
{//int *p = NULL;int cnt = 5;while(cnt){printf("I am child process, pid: %d, ppid:%d, cnt: %d\n", getpid(), getppid(), cnt);sleep(1);cnt--;//*p = 100;}
}int main()
{printf("I am father, pid: %d, ppid:%d\n", getpid(), getppid());pid_t id = fork();if(id == 0){// childChildRun();printf("child quit ...\n");exit(123);}LoadTask();// fatherwhile(1){int status = 0;pid_t rid = waitpid(id, &status, WNOHANG); // non blockif(rid == 0){usleep(100000);printf("child is running, father check next time!\n");DoOtherThing();}else if(rid > 0){if(WIFEXITED(status)){printf("child quit success, child exit code : %d\n", WEXITSTATUS(status));}else{printf("child quit unnormal!\n");}break;}else{printf("waitpid failed!\n");break;}}}

 

pid_t返回值 

设置了waitpid的WNOHANG后

  • 非阻塞等待会立刻返回,阻塞等待会等待子进程结束才会返回。
  • pit_t > 0 :等待成功,子进程退出了,并且父进程回收成功。
  • pit_t < 0 :等待失败。
  • pit_t == 0 :检测是成功的,只不过子进程还没退出,需要你下一次进行重复等待。

阻塞等待VS非阻塞等待

场景:张三找李四求助帮他复习期末考试。张三在李四的楼下等待李四就绪。

非阻塞等待:

  • 张三每隔几分钟就给李四打电话询问他是否就绪了
  • 张三在没有打电话的时间看书/游戏/抖音
  • 就绪的过程本质就是非阻塞等待。
  • 张三非阻塞等待李四过程 == 函数调用
  • 张三给李四打电话 == 函数传参
  • 李四说等着没好 == 函数的返回值
  • 每次函数调用的本质是检测李四的状态(是否就绪)
  • 立刻有返回值,多次等待,多次返回。
  • pid_ t waitpid(pid_t pid, int *status, WNOHANG);
  • pit_t == 0 :检测是成功的,只不过子进程还没退出,需要你下一次进行重复等待。
  • pit_t > 0 :等待成功,子进程退出了,并且父进程回收成功。
  • pit_t < 0 :等待失败。

阻塞等待:

  • 张三一直给李四打着电话,直到李四就绪,期间张三一直等待李四就绪,不敢别的事情。一直检测李四的状态(不就绪,就不返回)
  • 一直等待。直到子进程终止才返回。
  • pid_ t waitpid(pid_t pid, int *status, 0);
  • pit_t > 0 :等待成功,子进程退出了,并且父进程回收成功。
  • pit_t < 0 :等待失败。

🙂感谢大家的阅读,若有错误和不足,欢迎指正。下篇进入进程替换专题。


http://www.ppmy.cn/ops/38616.html

相关文章

Python ArcPy批量将大量栅格文件的投影坐标系转为地理坐标系

本文介绍基于Python语言中的ArcPy模块&#xff0c;批量将多个遥感影像由投影坐标系转为地理坐标系的方法。 在之前的文章中&#xff0c;我们介绍过将单独1景遥感影像的投影坐标系转为地理坐标系的方法&#xff0c;大家可以参考文章投影坐标系转为地理坐标系&#xff1a;GDAL命令…

手动实现简易版RPC(四)

手动实现简易版RPC(四) 往期内容 手动实现简易版RPC&#xff08;一&#xff09;&#xff1a;RPC简介及系统架构 手动实现简易版RPC&#xff08;二&#xff09;&#xff1a;简单RPC框架实现 手动实现简易版RPC(三)&#xff1a;mock数据生成 前言 接上几篇博客我们实现了最…

第一天复习Qt文件读取

Qt文件操作&#xff1a; 1、QFile QTextStream操作文件案例&#xff1a; 1、打开文件 QFile file(absolute filepath | relative path); file.readLine()返回内容长度&#xff0c;如果为-1就是读取失败 file. Close()读取后关闭 file.errorString()返回文件打开发生的错误2、…

面试:CopyOnWriteArrayList

问题&#xff1a; ArrayList 是线程不安全的&#xff0c;同一时间写和读会造成线程不安全&#xff0c;怎么解决呢&#xff1f; 答&#xff1a;可以使用CopyOnWriteList。 CopyOnWriteList特点 CopyOnWriteArrayList是Java中的一种并发集合类&#xff0c;它实现了List接口&am…

DNS 解析在网络传输中有什么意义?

首先我们先说说什么是DNS解析&#xff1f; DNS解析是将域名解析为对应的IP地址的过程。DNS它作为将域名和IP地址相互映射的一个分布式数据库&#xff0c;能够使人更方便地访问互联网。DNS解析的过程就是寻找哪个IP地址对应你所输入的网址&#xff0c;然后将网页内容返回给用户…

6份不用辞职就能赚钱的副业,上班族必看!

在这个经济浪潮中&#xff0c;生活成本的上升与工资增长的缓慢形成了鲜明对比。对于许多上班族来说&#xff0c;寻找额外收入的途径显得尤为迫切。 今天&#xff0c;就让我们一起探索那些适合在业余时间开展的副业&#xff0c;为你的财务自由之路添砖加瓦。 1. 闲鱼二手手机售卖…

nginx代理原理(端口复用)探究

前言&#xff1a;对于一些常用的插件&#xff0c;我们应该学会如何使用。同时&#xff0c;其实现原理也要进行深究&#xff0c;可以为其他的项目开发做借鉴。 探究方案&#xff1a; 一、发布两个不同的服务&#xff0c;这两个服务的端口不致 二、配置nginx&#xff0c;让这两…

TB交易开拓者旗舰版自动交易的设置

本文针对TB交易开拓者旗舰版V6.0.7.0(期货程序化交易软件下载 - 交易开拓者),目前网上没有自动交易设置的完整教程&#xff0c;特写此篇。 1. 设置期货账户的自动登录和登出。点击菜单“文件/系统设置”&#xff0c;然后在“安全”tab做如下设置&#xff1a; 2 设置你的期货账…