【Linux】进程控制(2)进程等待

hello~ 很高兴见到大家! 这次带来的是Linux系统中关于进程控制这部分的一些知识点,如果对你有所帮助的话,可否留下你宝贵的三连呢?
个 人 主 页 : 默|笙


文章目录

  • 一、进程等待
    • [1.1 进程等待的必要性](#1.1 进程等待的必要性)
    • [1.2 进程等待的方法](#1.2 进程等待的方法)
      • [1. wait方法](#1. wait方法)
      • [2. waitpid方法](#2. waitpid方法)
      • [3. 获取子进程status](#3. 获取子进程status)

接上次博客---->[进程控制(1)进程创建、终止]。


一、进程等待

1.1 进程等待的必要性

  1. 我们知道一个进程执行完了之后不会立即变成死亡状态,而是变成僵尸状态,如果这个进程的父进程对它不管不顾的话,那么它将一直存在于内存当中,占用系统内存也占用系统所分配的pid。
  2. 处于僵尸状态的进程是无法信号kill -9被杀死的,毕竟谁也无法杀死一个已经死掉的进程。
  3. 其次,父进程派给子进程执行的任务,任务完成的如何我们是需要知道的。比如子进程任务执行结果对不对,是不是正常终止,这部分退出信息是存储在它的task_struct里面。
  4. 既为了不让子进程一直处于僵尸状态(必须),也为了能够知道子进程任务完成的如何,我们需要让父进程对僵尸状态的子进程进行回收---回收子进程资源,获取子进程退出信息。
  • 退出信息储存在子进程的task_struct里面,,,,

1.2 进程等待的方法

1. wait方法

  1. 使用wait需要包含头文件sys/wait.h。
  2. 返回值:如果等待成功返回所等待的子进程的id,等待失败则返回-1。
  3. 关于参数stat_loc,这是一个输出型参数,用来获得子进程的状态,不关心则可以设置成NULL,之后会讲到。
  • 输出型参数:输出型参数的初始值并不重要,因为它是在调用函数后被函数赋值,成为一个函数执行结果的载体,可以通过这个参数获取结果,在这里就是子进程的退出状态。
cpp 复制代码
#include<sys/wait.h>
  2 #include<stdio.h>
  3 #include<unistd.h>
  4 #include<stdlib.h>
  5 
  6 int main()
  7 {
  8   pid_t id = fork();
  9   if (id == 0)
 10   {
 11     int cnt = 5;
 12     while(cnt--)
 13     {
 14       printf("我是一个子进程!pid:%d, ppid:%d\n", getpid(), getppid());
 15     }
 16     exit(0);//子进程执行完退出
 17   }
 18   sleep(10);
 19   pid_t sid = wait(NULL);
 20   if (sid > 0)                                                   
 21   {
 22     printf("我是一个父进程!pid:%d,我等待成功了,回收的是pid为%d>    的子进程\n",getpid(), sid);
 23   }
 24   return 0;
 25 }


  1. 执行程序之后,子进程执行代码会通过exit终止进程进入僵尸状态,为了不让父进程一下子给子进程回收掉,我让程序sleep了10s,10s过后父进程回收子进程,子进程死亡,父进程执行完代码被bash直接回收,进入死亡状态。
  2. wait方法里:在子进程执行的过程中,父进程会一直处于阻塞状态,父进程会一直等待子进程的结束好对其进行回收,不会做其他的事情。如果想验证可以让子进程休眠5s,让子进程执行的时间久一点,可以观察到父子进程都处于S+状态。
  • wait是回收任意一个已退出处于僵尸状态的子进程,如果没有则为阻塞等待状态。

2. waitpid方法

  1. 同样需要包含头文件sys/wait.h进行使用,不过与wait不同的是,它多了两个参数,一个pid,一个options。

  2. 第一个参数pid是要传给它目标子进程pid,然后它就只会等这个目标子进程,如果想回收任意子进程,就传-1

  3. stat_loc也是输出型参数,用来获取子进程的退出信息。

  4. options默认值为0,表示阻塞等待,即父进程等待子进程的这段时间什么事情都不干,就干等。如果传入WNOHANG(wait no hang不等待),表示非阻塞等待,也就是在等待子进程的时间里面父进程可以干其他的事情

  5. 返回值:在阻塞状态下等待成功则返回所等待子进程的pid,等待失败则返回-1;在非阻塞状态下,如果子进程还没有执行完成则返回0,不继续等待,若正常结束,返回子进程pid。

  • 第一个参数为-1,第三个参数为0时,与wait函数功能相同。
阻塞等待vs非阻塞等待
  1. 打个比方,我烧一壶水,烧这一壶水是需要时间的,阻塞等待就是我一直在这里等着这壶水烧开,这段时间里我什么也不干,就在它旁边守着直到听到它发出咕噜咕噜的声音知道它烧开为止;非阻塞等待就是我在烧这壶水的期间去干其他事情,干一会儿就回来看看水烧开没有,这样既可以等待水的烧开又可以干自己的事情。
  2. 要注意的是,等待的时间是取决于子进程的,无论是阻塞等待还是非阻塞等待要等的时间都是一样的。不存在非阻塞等待比阻塞等待效率高的说法,只能说是非阻塞等待干其他事情的效率提高了
cpp 复制代码
//非阻塞状态实验代码
1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 #include<sys/wait.h>
  5 
  6 int main()
  7 {
  8   pid_t id = fork();
  9   if (id == 0)
 10   {
 11     int cnt = 5;
 12     while(cnt--)
 13     {
 14       sleep(1);
 15       printf("我是一个子进程,pid:%d, ppid: %d\n", getpid(),getppid());
 16     }
 17     exit(0);
 18   }
 19   while(1)
 20   {
 21     pid_t sid = waitpid(id, NULL, WNOHANG);                      
 22     if (sid == 0)
 23     {
 24       printf("我是父进程,子进程还没执行完,我要干我自己的事情了\n");
 25       //写父进程等待期间要执行的代码
 26     }
 27     else if (sid > 0)
 28     {
 29       printf("我是父进程,我回收成功了!pid:%d\n", getpid());
 30       exit(0);
 31     }
 32   }
 33   return 0;
 34 }
  1. 执行非阻塞等待,第三个参数传入WNOHANG,父进程要使用一个while循环,去不断的使用waitpid()函数进行等待,如果返回值为0则干父进程自己的事情,返回值不为0就回收完毕,父进程终止。差不多就是下面的样子。

3. 获取子进程status

  1. wait函数和waitpid函数都有一个参数status,一个输出型参数,用来获得系统给的子进程的退出信息。
  2. 如果传递的是NULL,则默认不关心子进程的退出状态信息。如果传递的是一个整型变量,那么系统给的子进程的退出信息就会在调用wait/waitpid函数之后存储在这个整型变量里面。
  1. 系统给的退出信息不是一个简单的整数,应该把它当作位图来看待,一个整数4个字节是32bit位,但是退出信息只会存储在16个比特位里面,最高16位一般不会使用。
  2. 在这16个比特位里面,如果是正常终止,那么第8~15个比特位里面会存储子进程的退出状态(0~255),第7位则固定为0,第0~6位为未使用(全为0);如果是被信号所杀,那么第8~15位是没有使用的(全为0),第7位存储的是core dump标志,第0~6位存储的则是终止子进程的信号编号。
  3. 我们可以通过位运算来获取系统给的退出信息,也可以使用系统给的宏来获取:
  1. WIFEXITED(status): 若为正常终止进程返回的状态,则为真(1)。对应(status & 0x7F) == 0,如果信号为0,说明正常终止了。0x7F = 0111 1111。
  2. WEXITSTATUS(status) : 若WIFEXITED非零,提取子进程退出码。(查看进程
    的退出码)对应(status >> 8) & 0xFF,向左移动8位进行提取。0xFF = 1111 1111。
cpp 复制代码
 1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 #include<sys/wait.h>
  5 
  6 int main()
  7 {
  8   pid_t id = fork();
  9   if (id == 0)
 10   {
 11     int cnt = 5;
 12     while(cnt--)
 13     {
 14       sleep(1);
 15       printf("我是一个子进程,pid:%d, ppid: %d\n", getpid(), getppid());
 16     }
 17     exit(0);
 18   }
 19   while(1)
 20   {
 21     int* status = NULL;
 22     pid_t sid = waitpid(id, status, WNOHANG);                                                                                            
 23     if (sid == 0)
 24     {
 25       // printf("我是父进程,子进程还没执行完,我要干我自己的事情了\n");
 26       //写父进程等待期间要执行的代码
 27     }
 28     else if (sid > 0)
 29     {
 30       printf("我是父进程,我回收成功了!pid:%d\n", getpid());
 31       printf("我是父进程,WIFEXITED:%d, WEXITSTATUS:%d\n", WIFEXITED(status), WEXITSTATUS(status));
 32       exit(0);
 33     }
 34   }
 35   return 0;
 36 }
  1. 正常终止,WIFEXITED返回值为1,WEXITSTATUS有意义,返回值为0,代表退出码为0,结果正确。

今天的分享就到此结束啦,如果对读者朋友们有所帮助的话,可否留下宝贵的三连呢~~
让我们共同努力, 一起走下去!

相关推荐
Irissgwe6 小时前
九、Linux信号机制(二)
linux·进程·可重入函数·volatile·sigchld信号·进程信号
野熊佩骑6 小时前
一文读懂Nginx 之 Ubuntu使用apt方式安装Nginx官方最新版本
linux·运维·服务器·nginx·ubuntu·http
顶点多余7 小时前
多路转接--select /poll
运维·服务器
老毛肚7 小时前
微服务网关整合授权中心实现单点登录
运维·微服务·架构
小梦爱安全7 小时前
配置RIP动态路由协议
运维·网络
闫记康7 小时前
Linux学习day3
linux·服务器·学习
墨着染霜华7 小时前
Windows 启动 Nginx 一闪而过、pid 丢失、logs 目录报错彻底解决
运维·windows·nginx
皆圥忈7 小时前
Linux 进程管理从入门到实战(一)
linux
雪度娃娃7 小时前
Asio——socket的创建和连接
linux·运维·服务器·c++·网络协议
剑神一笑8 小时前
Linux tar 归档命令深度解析:从文件打包到压缩算法的完整实现
linux·运维·服务器