目录
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从运行队列移动到等待队列中,不再被调度,而是等待
- 本质上是等待某种条件发生。
- 软件条件满足(子进程退出)
- 硬件资源就绪(scanf键盘输入数据发生)
options&非阻塞等待√
在子进程运行期间,父进程除了等待子进程或者是休眠,能不能干点其他的事情❓
- 当然可以,在父进程等待,阻塞状态。可以通过设置options来让父进程干点事情。不阻塞等待而是非阻塞等待。
什么又是非阻塞等待呢❓用代码该怎么去实现呢❓
**非阻塞等待(Non-blocking Wait)**则与阻塞等待相反。当线程或进程在等待某个条件满足或某个操作完成时,它不会暂停执行其他任务,而是会继续执行后续的任务。也就是说,即使等待的条件还没有满足或操作还没有完成,线程或进程也不会被阻塞,而是会继续执行其他的操作。
通过设置options的宏值WNOHANG(wait no hang 等待没有阻塞 = 非阻塞等待)
在计算机中,"HANG" 通常指的是程序或系统出现无响应或停顿的状态,也就是常说的“卡住”或“死机”。当程序或系统由于某种原因(如资源锁定、死循环、死锁或外部系统交互问题等)而无法继续正常执行时,就可能会出现"HANG"的情况。这种情况下,用户可能无法与程序或系统进行交互,需要等待程序或系统恢复正常或进行重启操作。另外,在一些特定的语境下,"HANG" 也可能被用来描述服务器或数据库的某些服务出现故障或无法访问的情况,这也可以被视为一种"宕机"现象。在这种情况下,"HANG" 指的是服务器或数据库的服务因为某种原因而停止响应或无法提供服务。
具体操作
- options这个参数只要一设置就会出现非阻塞等待。
- 设置waitpid的WNOHANG本质上是检测一次进程的状态变化。
- 调用一次waipid就检测一次。每次调用都是检测,多次调用多次检测。
- 非阻塞等待调用多次waitpid,调用waitpid检测是否退出等待过程无问题,只是子进程还未终止,需要等待下次等待。
- 综上:非阻塞等待的时候 + 循环 = 非阻塞轮询
1: myprocess.c
1 #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 //father
30 //父进程,父进程在子进程运行期间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 else
48 {
49 printf("child quit unnormal!\n");
50 }
51 printf("wait success,rid: %d\n",rid);
52 break;
53 }
54 else
55 {
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)
{
// child
ChildRun();
printf("child quit ...\n");
exit(123);
}
LoadTask();
// father
while(1)
{
int status = 0;
pid_t rid = waitpid(id, &status, WNOHANG); // non block
if(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 :等待失败。
🙂感谢大家的阅读,若有错误和不足,欢迎指正。下篇进入进程替换专题。