当前位置: 首页 > 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

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

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

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

http://www.lryc.cn/news/211328.html

相关文章:

  • python设计模式笔记1:创建型模式 工厂模式和抽象工厂模式
  • 第五章 I/O管理 一、I/O设备的基本概念和分类
  • vue3动态引入图片(:src)
  • Android-登录注册页面(第三次作业)
  • [论文精读]How Powerful are Graph Neural Networks?
  • Redis实现分布式锁之----超时和失效(非原子性)问题----解决方案
  • Android使用Hilt依赖注入,让人看不懂你代码
  • ZYNQ连载01-ZYNQ介绍
  • 第十节——Vue组件
  • Redis(01)| 数据结构
  • SpringBoot工程启动时自动创建数据库、数据表
  • Uni-app智慧工地可视化信息平台源码
  • 计算机网络重点概念整理-第五章 传输层【期末复习|考研复习】
  • Java毕业设计 SpringBoot 新能源充电桩管理系统
  • JNI接口
  • 国内好用的免费ai软件
  • MAC缓解WebUI提示词反推
  • 【设计模式之原型模式 】– C++
  • Flask路由机制分析之二
  • vue中如何获取当时时间时分秒
  • matlab simulink 直线一级倒立摆控制(自起摆和稳态控制)
  • Transformers实战(二)快速入门文本相似度、检索式对话机器人
  • 【错误解决方案】ModuleNotFoundError: No module named ‘PeptideBuilder‘
  • 汇编学习(1)
  • C#,数值计算——分类与推理Svmlinkernel的计算方法与源程序
  • 【鸿蒙软件开发】ArkTS容器组件之Badge
  • H5游戏源码分享-命悬一线
  • 【电路笔记】-交流电阻和阻抗
  • android开发使用OkHttp自带的WebSocket实现IM功能
  • 前端小技巧: TS实现柯里化函数