Linux 进程等待

目录

进程等待

进程等待的必要性

进程等待的方法

wait()

waitpid()

参数int* status


进程等待

                                                                            

谁要等待? 等待什么? 为什么要等待 ?

首先要知道进程终止或退出的时候会发生什么,  进程退出时会关闭所有文件描述符, 释放在用户空间分配的内存, 但是PCB却会暂时保留, 里面存着退出状态, 比如一个进程正常退出, PCB里面就放着进程的退出状态(也就是退出码). 如果是异常退出( 前面说到, 肯定是收到信号了), 那么PCB里面存放着导致该进程终止的信号.

子进程的退出状态讲道理是该由父进程回收的( 自己生的孩子, 自己就得负责 ), 也就是说, 父进程必须得等待子进程退出接收子进程的退出状态. 如果子进程的退出状态父进程一直没有获取, 那么此时子进程就处于僵死状态, 成为僵尸进程 . 

回答开头的问题: 父进程要等待;  等待子进程退出;  为了拿到子进程的退出状态, 防止子进程一直处于僵尸状态

进程等待的必要性

  • 要是父进程先于子进程退出, 那么子进程就成了孤儿进程, 1号进程(福利院院长)会立即领养, 但1号进程要想回收, 还是得等子进程退出后再回收.
  • 要是子进程先于父进程退出, 退出时为了保存退出状态 , 子进程一些资源并没有释放, 如果此时父进程没有关注到子进程的退出(也就父进程是没有拿到子进程的退出状态), 子进程伤心欲绝, 就会变成 "僵尸进程",变成一个连kill -9 都杀不死的进程, 因为谁也杀不死一个已经死去的进程 (只有重新来过, 也就是重启机器才能解诀僵尸进程),  "僵尸进程" 会造成资源泄露, 占用进程数的问题 .
  • 僵尸进程和孤儿进程详细在另一篇博客
    戳链接( ̄︶ ̄)↗https://blog.csdn.net/qq_41071068/article/details/103231310

简单来说, 就是父进程需要知道, 派给子进程的任务完成的如何, 结果对还是不对,或者是否正常退出. 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息. 

说了一大堆, 如何等待

进程等待的方法

  • wait()

头文件 : sys/wait.h

pid_t  wait (int* status)

返回值:  成功返回被等待进程PID,  失败则返回 -1. (成功指的是被子进程正常退出, 失败指的是被子程序异常终止)
参数:  int* status, 是输出型参数,获取子进程退出状态, 将其退出状态存到status所指向的内存空间, 不关心被子进程退出状态则可以设置status为NULL

wait() 是阻塞的, 在wait 返回之前, 阻塞等待子进程退出, 再返回 .

阻塞与非阻塞

  • 阻塞:   为了完成一个功能, 若发起调用, 当前不具备完成条件, 则一直等待达到完成条件, 调用才会结束
  • 非阻塞: 与阻塞相反, 若发起调用, 当前不具备完成条件, 立即返回
  • 最大区别是, 调用是否立即返回
  • waitpid()

pid_ t  waitpid (pid_t pid, int *status, int options

参数:
pid :

  • pid = -1 :等待任一个子进程
  • pid >  0 : 等待其进程ID与pid相等的子进程。

int* status:

  • WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
  • WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)

后面详细看status和WIFEXITED, WEXITSTATUS这两个宏

options: options = 1时, waitpid是非阻塞的, options = 其他值时 waitpid() 是阻塞的

  • WNOHANG: 值为1 的宏 

返回值

有三种返回值
=-1 : waitpid()出错
= 0 : 没有等到子进程退出
>0 :  等到子进程退出, 返回子进程PID

详细来看:

  • 给 options 传WNOHANG 或 1, 此时waitpid()是非阻塞的,
    1. 如pid此时指定,即pid>0, 如指定pid的进程在调用waitpid()时退出,  waitpid()返回子进程PID, 如发现指定pid的进程
        还没有退出, 不予等待, 直接返回 0.
    2. 若pid不指定,即pid = -1,若在waitpid()调用之前已有子进程退出(任意子进程都可以), 则返回该子进程的PID,
        若在调用waitpid()之前没有任何子进程退出, 则返回 0
  • 若给options传入非1的值, 如等不到子进程退出, 会一直阻塞
  • 如果调用waitpid()出错,  则返回-1, 这时errno会被设置成相应的值以指示错误所在 .

PS:  当waitpid是waitpid(-1, &status, 非1) 等效 wait (&status)
       不关心子进程退出码时 waitpid(-1, NULL, 非1) 等效 wait (NULL)

参数int* status

前面说到. wait/waitpid都可以获取到退出的子进程的退出状态, 那么获取到的状态在哪儿保存呢 ?

这就需要我们事先申请一块内存空间来保存, 即 int status; 用这个int型变量来保存退出状态, 那么wait/waitpid又如何知道, 要将退

出状态给我们事先申请好的这个变量呢? 传入这个变量的指针, 由操作系统填充.

前面说到, 进程在退出时, 会有正常退出和异常退出(终止), 那么一个int 型变量如何保存这两种不同些情况的的退出状态呢 ?

如下图:

可以看到, 在正常退出时, status这个int型变量, 只有低16位用到了

即, 在正常退出时, 低16位中的高8位存的是子进程得退出码, 后8位全为0,

在异常退出时, 底7位存的是进程的异常退出信号值, 第8位是core dump 标志, 即核心转储文件标志, 为了用户方便查看异常终止

的信息, 此时低16位中的高8位未用

所以, 在wait/waitpid结束后,  要先取出dtatus的低7位看子进程是不是异常终止, 如果不是, 再来看退出码, 如下:

if (!(status & 0x7f)) {
    printf("子进程退出码为\n", (status >> 8) & 0xff);
}
else {
    printf("子进程异常终止\n");
}

这样写很麻烦, 而且还容易出错, 所以系统就将 !(status & 0x7f) 和 (status >> 8) & 0xff 封装成了两个宏

WIFEXITED WEXITSTATUS , 既上面的代码可以写成如下:

if (WIFEXITED(status)) {
    printf("子进程退出码为\n", WEXITSTATUS(status));
}
else {
    printf("子进程异常终止\n");
}

举栗子

wait.c 

用wait() 模拟父母接孩子的场景, 父母就像wait() 阻塞一样, 不管多久, 都会等着我们, 直到接到我们.

#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include<stdlib.h>
int main(){
    printf("一位爸爸来学校接孩子放学了~\n");
    pid_t pid = fork();
    if(pid < 0){
        perror("fork erro");
        exit(-1);
    }
    else if(pid == 0){
        sleep(30);
        exit(30);
    }
    int status = -1;
    wait(&status);
    if (!(status & 0x7f)) {
        printf("%d分钟后接上了孩子\n", (status >> 8) & 0xff);                
    }
    else {
        printf("孩子在幼儿园发烧送医院了!\n");            
    }
    return 0;
}

在30秒后, 打印出了30分钟后接上了孩子 

如果我们在另一个窗口下用kill -9 杀死子进程, 此时子进程就是异常终止了, 运行如下:

 waitpid.c

用waitpid模拟出租车司机在车站等待拉乘客的场景, 出租车司机会一直去询问旅客乘不乘车, 当长的时间拉不到人, 就离开去别的地方拉乘客了

#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include<stdlib.h>
int main(){
    printf("一位出租车司机在车站拉乘客~\n");
    pid_t pid = fork();
    if(pid < 0){
        perror("fork erro");
        exit(-1);                  
    }
    else if(pid == 0){
        sleep(5);
        exit(5);                           
    }
    int status = -1;
    while(waitpid(-1, &status, WNOHANG) == 0){
        printf("没有人打车, 继续等...\n");
        sleep(1);                           
    }
    if(WIFEXITED(status)){
        printf("这次等了%d分钟才拉到乘客(子进程退出码)\n", WEXITSTATUS(status));
        printf("有人打车了, 出发~\n");
    }
    else{
        printf("没有等到乘客(子进程异常退出)\n");
    }
    return 0;

}

发布了223 篇原创文章 · 获赞 639 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/qq_41071068/article/details/103302883
今日推荐