文章目录

  • 进程等待
    • 进程等待的必要性
    • wait函数
    • waitpid函数

进程等待

进程等待的必要性

我们之前提到过僵尸进程,僵尸进程就是子进程先于父进程退出,子进程的退出状态信息发送给父进程但是父进程忽略处理,子进程就变成了僵尸进程,解决僵尸进程我们有三种办法, 第一就是杀死父进程,子进程就变成了孤儿进程,会被一号进程领养,一号进程会回收子进程的退出状态信息;第二种就是关机,操作系统一关,所有进程都没了;第三种就是今天的要介绍的进程等待。

父进程进行进程等待,等待子进程退出之后,回收子进程的退出状态信息,防止子进程变成僵尸进程。

wait函数

函数原型:
pid_t wait(int *status);

返回值:等待成功返回被等待进程的pid,等待失败返回-1.
参数:输出型参数,获取子进程的退出状态信息,如果不关心子进程的退出状态信息可以设置为NULL。
status是wait函数填充的,程序员在wait函数调用结束之后,可以通过参数获得退出进程的退出状态信息。
wait函数是一个阻塞调用函数(一定等待到被等待进程的退出状态信息才会结束函数调用)

我们来验证一下wait函数的阻塞属性:

上面的代码有两个进程,父进程和子进程,父子进程相互独立,抢占式执行,所以就有了两种可能性,第一就是父进程先执行,子进程后执行,此时子进程有可能变成僵尸进程,如果子进程没有变成僵尸进程,那就说明父进程的wait函数在等待中,是具有阻塞属性的;第二种是子进程先执行,父进程再执行,这种情况,wait等到到子进程的退出状态信息就很顺理成章了。

没有产生僵尸进程,说明两种情况下,wait都等待到了。
我们再让子进程睡眠十秒,来看一下效果:

运行结果:

子进程并没有变成僵尸进程,我们查看父进程的调用堆栈,能看到父进程正在苦苦等待子进程结束。

pstack:查看进程的调用堆栈,也就是说,用来查看进程正在运行什么代码。

我们回到wait函数的参数,是一个指向int类型的指针,我们说它是输出型参数,它指向的那个int类型的数字包含了wait向程序员传递的退出进程的退出状态信息。它一共有四个字节,不过,包含进程退出状态信息的只有后面两个字节,其中一个字节包含了退出码,两外一个字节的第一个比特位是coredump标志位,后七个比特位是退出信号位。

那如何通过进程退出状态信息判断进程是否是正常退出呢?
标准是看退出信号是否有值,如果退出信号等于0,那就是正常终止,只有正常终止才会有退出码,如果退出信号大于0,那就是异常终止,我们上次提到的那个段错误,它前面就有一个信号11,这种就是异常终止。

coredump:标志着是否产生核心转储文件,不标志进程是否异常终止。如果该比特位是1,那就是产生了核心转储文件,程序确实是异常终止。程序异常终止的时候,理论上是要产生核心转储文件的,不过此时还要关心core file size的值,如果该值是0,就有一个软限制,不允许产生核心转储文件,所以此时该比特位值是0,但是进程依然是异常终止,修改为unlimited就可以在异常终止的时候产生coredump文件,比特位就会变成1了。

我们如何通过wait的参数得到退出信号呢?

退出信号:status & 0x7f
coredump标志位:(status>>7) & 0x1
退出码:(status>>8) & 0xff

我们通过下面这段代码来举例说明一下:

运行结果:

运行结果:

waitpid函数

函数原型:

pid_t waitpid(pid_t pid, int *status, int options);

参数:

  • pid:

    • pid==-1,等待任意一个子进程(一个父进程可以有多个子进程),与wait等效
    • pid>0,等待进程ID与pid相等的进程。
  • status,进程退出状态信息,和wait函数的参数相同
  • options:waitpid函数可以有非阻塞属性,该属性就是通过该参数设置的。如果option参数为WNOHANG,就是设置waitpid函数为非阻塞状态,如果pid指定的子进程没有结束,则waitpid()函数返回0,不会一直等待,如果pid指定的子进程正常结束,就返回该子进程的ID。如果设置为0,那就是阻塞的,和wait函数的阻塞属性一样。
    如果是非阻塞调用函数,有一个很明显的问题就是我们不知道函数是否完成了既定的功能,所以 非阻塞调用一定要搭配循环使用 。

waitpid函数返回值:

  • 正常返回的时候waitpid返回收集到的子进程的进程号
  • 如果设置了选项WNOHANG,调用中waitpid发现没有已经退出的子进程可以收集,则返回0
  • 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在。

验证waitpid函数的阻塞以及waitpid函数的等待功能:

waitpid函数被设置了阻塞属性,一直等待到子进程结束之后等待到子进程的退出状态信息才结束调用,且我们查看进程状态的时候,父进程确实在等待子进程。且子进程并没有变成僵尸进程,说明子进程的退出状态信息被父进程成功回收。

验证waitpid可以有的非阻塞属性:

子进程变成了僵尸进程,父进程陷入睡眠当中,验证了waitpid的非阻塞属性。
我们前面提到,在调用非阻塞属性的函数时,要使用循环,循环可以保证非阻塞函数完成我们想要的功能。
我们来验证一下waitpid函数的非阻塞属性,加上循环,通过循环来让它持续等待。

#include<stdio.h>                                               2 #include<unistd.h>3 #include<stdlib.h>4 #include<sys/wait.h>5 int main(){6   pid_t ret = fork();7   if(ret < 0){8     return 0;9   }else if(ret == 0){10     //child;11     printf("I am child.\n");12     sleep(30);13     printf("I am child,I exited....\n");14   }else{15     //father16     while(1){17       int w_ret = waitpid(-1,NULL,WNOHANG);18       //返回值:19       //正常情况:20       //    >0:返回等待到的子进程的PID21       //    =0:没有等待到22       //不正常情况下返回-123       if(ret == w_ret){24         break;25       }else if(w_ret == -1){26         return 0;27       }28     }29     printf("I am father.\n");30     while(1){31       sleep(1);32     }33   }34   return 0;35 }

父进程成功回收到子进程的退出状态信息,在等待到子进程的退出状态信息之前父进程一直在执行waitpid函数,最后子进程也没有变成僵尸进程,说明waitpid函数成功完成函数功能。

当非阻塞遇到循环,不执行完功能就会一直循环,那其他功能要怎么实现呢?比如父进程的守护,这个要用到进程信号的知识,我们后面再给出答案。

【Linux】进程控制2-进程等待相关推荐

  1. Linux下的C编程实战(开发平台搭建,文件系统编程,进程控制与进程通信编程,“线程”控制与“线程”通信编程,驱动程序设计,专家问答)

    Linux下的C编程实战(一) ――开发平台搭建 1.引言 Linux操作系统在服务器领域的应用和普及已经有较长的历史,这源于它的开源特点以及其超越Windows的安全性和稳定性.而近年来,Linux ...

  2. 广州大学学生实验报告,进程控制与进程通信

                                                    广州大学学生实验报告 开课学院及实验室: 计算机科学与网络工程学院  电子楼418B        20 ...

  3. 【Linux】进程控制(进程创建、进程终止、进程等待、进程替换)

    文章目录 一.进程创建 1.1 系统调用 fork 1.2 理解 fork 的返回值 1.3 写时拷贝策略 二.进程终止 2.1 main 函数的返回值 2.2 进程退出的几种情况 2.3 进程退出码 ...

  4. Linux 进程控制(创建/退出/等待/替换)

    目录 进程创建 fork()函数 fork返回值 fork写时拷贝 fork失败原因 fork用法 进程退出 退出场景 常见的退出方法 正常退出 异常退出 _exit()系统调用 exit()函数 _ ...

  5. Linux 进程控制 :进程创建,进程终止,进程等待,程序替换

    进程创建 进程终止 进程等待 程序替换 进程创建 fork函数 创建一个子进程,父子进程代码共享,数据独有 #include <unistd.h> pid_t fork(void); 返回 ...

  6. Linux系统编程之进程控制(进程创建,fork函数,进程中止,进程等待,程序替换)

    进程创建 fork()------复制,返回值,写时复制 vfork()创建子进程-子进程与父进程共用同一块虚拟地址空间, 为了防止调用栈混乱,因此阻塞父进程直到子进程调用exit()退出或者进行程序 ...

  7. 【Linux】linux进程--进程控制:进程创建、进程终止、进程等待、进程程序替换

    目录 1.进程创建 1)重温fork():让正在运行的进程创建出来一个子进程:从已存在的进程中创建一个新的进程,新进程为子进程而远进程为父进程. 2)fork内部完成的事情 3)用户空间 & ...

  8. Linux系统编程17:进程控制之进程等待为什么进程需要被等待wait方法和waitpid方法阻塞和非阻塞等待

    文章目录 (1)为什么子进程需要被等待 (2)等待进程的方法 A:wait方法 B:waitpid方法 C:进程非阻塞式等待 前文说过,子进程被创建之后,父子进程究竟谁先运行是由调度器说了算. 但是, ...

  9. Linux_进程控制(创建进程,等待进程,进程终止)

    文章目录 1.创建进程 1.1 fork()函数初识 1.2 fork()创建进程代码示例 2.等待进程 2.1 进程等待概念 2.2进程等待必要性 2.3 进程等待方法 2.3.1 wait 2.3 ...

最新文章

  1. 生物系统和疾病的多组学数据整合考虑和研究设计
  2. 第28讲 | 弄懂数字货币交易平台(二)
  3. ubuntu系统debootstrap的使用
  4. CTFshow php特性 web115
  5. leetcode 190. 颠倒二进制位(位运算)
  6. 最耐用的手机盘点 网友:我这个能用到品牌商“破产”!
  7. java hash简易_Java手写简易版HashMap的使用(存储+查找)
  8. sqlserver中分区函数 partition by的用法
  9. c51语言中 位取反指令,C51的几种位操作运算说明
  10. 在做模具设计过程中应注意哪些问题
  11. Windows系统利用zip命令压缩文件夹
  12. Android使用蓝牙录音和播放
  13. Excel如何安装VBA?
  14. 【那些年,我们一起追的女孩】第十四章
  15. Problem B: 小度挑战赛
  16. php爬取房源,用python爬取二手房交易信息并进行分析
  17. TK1上如何安装teamviewer,完结
  18. iOS中AppTrackingTransparency(ATT)设置方法和注意事项
  19. Bugku杂项——论剑
  20. 树莓派自启动python程序,远程树莓派桌面,重装树莓派系统,串口驱动,永不休眠,树莓派旋转屏幕方向,树莓派定时重启,修改矫正树莓派时间,raspbian Linux 系统命令常用

热门文章

  1. protel DXP软件应用技巧
  2. GB28181协议级联在公安多级网络应用的解决方法
  3. 微信小程序:王者荣耀出装与铭文推荐助手
  4. 亚马逊数据分析选品的几个维度你都知道吗?
  5. 使用微软翻译 Microsoft Azure
  6. 三坐标检测之元素的测针半径补偿
  7. 流程DEMO-制度发文和干部任免
  8. 提升串口服务器的稳定性,带DTU功能的十六串口服务器
  9. 获取微信公众号关注页面地址
  10. android 手机nfc地铁,nfc怎么刷地铁