【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,结果正确。

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

相关推荐
ん贤2 小时前
io.copy
运维·服务器·网络·io.copy
2401_890443022 小时前
Linux EXT系列文件系统
linux
菜鸡00012 小时前
安装glog
linux
jockerzoo@2 小时前
IP 定向灰度发布:ArgoCD + GitLab CLI 方案
运维
旖旎夜光2 小时前
Linux(5)(上)
linux·学习
乾元2 小时前
红队 / 蓝队:用 AI 自动生成攻击场景并评估防御效果——从“安全演练”到“可计算的网络对抗系统”
运维·网络·人工智能·网络协议·安全·web安全·架构
半路_出家ren2 小时前
Python操作MySQL(详细版)
运维·开发语言·数据库·python·mysql·网络安全·wireshark
lbb 小魔仙2 小时前
eBPF+Linux 6.18:云原生环境下的安全监控与故障排查实战
linux·运维·云原生
Wzx1980122 小时前
go聊天室项目docker部署
运维·docker·容器