进程同步的两个函数---wait()和waitpid()

一.简要介绍( wait() ):

    1.wait() :

pid_t wait(int *status);

       1> wait() 的功能是阻塞等待子进程终止, 回收子进程资源, 调用 wait() 函数的进程会被挂起, 进入阻塞状态, 直到它的某个子进程变为僵尸态,但是该子进程的 PCB 还保留,内核在其中保留了一些信息:(1)若为正常终止,保留退出状态 (2)若为异常终止,保留使其终止的信号(关于退出的状态,我们可以在 shell 中使用特殊变量 $?来进行查看),父进程接收子进程退出状态信息,同时彻底清除掉这个进程。

          父进程调用 wait() 或者waitpid() 函数用 status 传出参数来接收状态信息

             若值为 -1 ,则回收失败;

             若值为清除掉的子进程  id ,则回收成功

      2> 父进程调用 wait() 函数可以回收子进程终止信息,此函数有三个作用:

     (1)阻塞等待子进程退出

     (2)回收子进程残留资源

     (3)获取子进程结束状态(退出原因)

      3>使用 wait() 传出参数 status 当其不空时(可以为空),来保存进程的退出状态, 退出状态存放在参数 status 的低 8 位中, 可通过 Linux 系统中提供的一组宏函数来获取,进一步判断进程终止的具体原因。 它们的参数与wait()函数相同, 都是一个整型的 status。 宏函数(常用的几个宏)的功能分别如下:
     (1)WIFEXITED(status): 非0  ->  正常退出;

              WEXITSTATUS(status): 如果上宏为真,则使用此宏 -> 获取进程退出状态(exit的参数);

     (2)WIFSIGNALED(status):  非0 -> 进程异常终止;

              WTERMSIG(status):如果上宏为真,则使用此宏 -> 获取使进程终止的那个信号的编号;


实例二. ( wait() )

1.利用   两种特殊的进程---孤儿进程和僵尸进程 中的僵尸进程实例加以 wait() 改写(未使用宏):

#include"stdio.h"
#include"stdlib.h"
#include"unistd.h"
#include"sys/wait.h"
int main(){

        pid_t pid,wpid;
        printf("Begin...\n");
        pid = fork();

        if(pid == -1){    // pid 值为-1,则创建子进程失败
                perror("fork error!");
                exit(1);
        }
        else if(pid == 0){    // pid 值为0,则创建子进程成功

                printf("i am child,pid = %u,ppid = %u\n",getpid(),getppid());
                sleep(3);
                printf("child goes to die...\n");
        }
        else{    // pid 值为非负整数,则为父进程
                wpid = wait(NULL);
                if(wpid == -1){
                        perror("wait error!");
                        exit(1);
                }
                int i;
                for(i=1;i<1000;i++){
                        printf("i am parent,pid = %u,ppid = %u\n",getpid(),getppid());
                        sleep(1);
                }
        }

        return 0;
}

   在执行代码的过程中,开启另一个终端(terminal),并输入以下命令:

ps xua

   将该结果同之前的僵尸进程结果截图进行比较,发现 子进程已经被回收

   2.正常退出(使用正常退出宏)实例:

#include"stdio.h"
#include"stdlib.h"
#include"unistd.h"
#include"sys/wait.h"
int main(){

        pid_t pid,wpid;
        int status;
        printf("Begin...\n");
        pid = fork();

        if(pid == -1){    // pid 值为-1,则创建子进程失败
                perror("fork error!");
                exit(1);    
        }
        else if(pid == 0){    // pid 值为0,则创建子进程成功

                printf("i am child,pid = %u,ppid = %u\n",getpid(),getppid());
                sleep(3);
                printf("child goes to die...\n");
                exit(11);    // 给子进程一个退出值
        }
        else{    // pid 值为非负整数,则为父进程
                wpid = wait(&status);
                if(wpid == -1){
                        perror("wait error!");
                        exit(1);
                }
                if(WIFEXITED(status)){
                        printf("child exits with %d\n",WEXITSTATUS(status));
                }
                int i;
                for(i=1;i<100;i++){
                        printf("i am parent,pid = %u,ppid = %u\n",getpid(),getppid());
                        sleep(1);
                }
        }

        return 0;
}

    由结果可知,当我们在代码中,让子进程以 exit(11) / return 11 正常退出时,第一组宏可获取子进程的退出状态

  3.异常退出(使用异常退出宏):

#include"stdlib.h"
#include"unistd.h"
#include"sys/wait.h"
int main(){

        pid_t pid,wpid;
        int status;
        printf("Begin...\n");
        pid = fork();

        if(pid == -1){    // pid 值为-1,则创建子进程失败
                perror("fork error!");
                exit(1);
        }
        else if(pid == 0){    // pid 值为0,则创建子进程成功

                printf("i am child,pid = %u,ppid = %u\n",getpid(),getppid());
                sleep(60);        // 想要在 60 秒 手工杀死程序
                printf("child goes to die...\n");
                exit(11);
        }
        else{    // pid 值为非负整数,则为父进程
                wpid = wait(&status);
                if(wpid == -1){
                        perror("wait error!");
                        exit(1);
                }
                if(WIFEXITED(status)){
                        printf("child exits with %d\n",WEXITSTATUS(status));
                }
                if(WIFSIGNALED(status)){
                        printf("child is killed by %d\n",WTERMSIG(status));
                }
                int i;
                for(i=1;i<100;i++){
                        printf("i am parent,pid = %u,ppid = %u\n",getpid(),getppid());
                        sleep(1);
                }
        }

        return 0;
}

在执行代码的过程中,开启另一个终端(terminal)到相应目录下,并输入以下命令查看id号:

ps xua

   并且执行下列命令将该子进程杀死并转回原先 terminal 查看:

kill 9 4805

   由结果可知,当我们用 9 号信号杀死子进程的时候,使用第二组宏,可获取使该进程终止的信号的编号;当然,我们也可以使用别的信号来杀死子进程,输入下列命令,显示信号列表如下:

kill -l


三.简要介绍( waitpid() ):

1.waitpid():

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

  1> 参数 pid 一般是进程的 pid, 但也会有其它取值。 参数 pid 的取值及其意义分别如下:
        > 0 时, 回收指定  id 的子进程;
        = -1 时,回收任意子进程( 作用同 wait() );
        = 0 时, 回收和当前调用 waitpid() 一个组的所有进程;
     < -1 时, 等待指定进程组中的任何子进程, 进程组的 id 等于 pid 的绝对值。

  2> 参数 status , 同 上面的 wait() 一样,也可结合宏使用

  3>参数 options ,options 提供控制 waitpid()的选项, 该选项是一个常量, 或由“ |” 连接的两
      个常量。 该选项支持的选项如下:
(1) WNOHANG。 即使子进程没有终止, waitpid()也会立即返回,同阻塞回收
(2) WUNTRACED。 如果子进程暂停执行, 则 waitpid()立刻返回。

 4>waitpid()函数的返回值会出现 3 种情况:
(1) 正常返回时 -> 获取子进程的 pid;
(2)若 options 的值为 WNOHANG, 但调用 waitpid()时发现没有已退出的子进程可收集,
         -> 则返回 0;
(3) 若调用过程出错, 返回-1。 errno 会被设置成相应的值以指示错误位置。


 四.实例

1.阻塞方法回收指定 pid 的子进程,代码及结果如下:

#include"stdio.h"
#include"stdlib.h"
#include"unistd.h"
#include"sys/wait.h"
int main(){

        pid_t pid,wpid;
        printf("Begin...\n");
        int i = 0;
        for(i = 0;i < 5;i++){

                pid = fork();
                if(pid == -1){
                         perror("fork error!");
                         exit(1);
                 }
                 else if(pid == 0){
                         break;
                }
                else if(i == 3){
                        wpid = pid;
        }
        }
        if(i < 5){
                sleep(i);
                printf("i am %d child,pid = %u\n",i+1,getpid());
        }
        else{
                sleep(i);
                printf("i am parent!\n");
                waitpid(wpid,NULL,0);    // 用 waitpid() 方法来回收指定 pid 为第四个的子进程,第三个参数为 0 ,默认为 阻塞 回收
                while(1);
        }

        return 0;
}

 在执行代码的过程中,开启另一个终端(terminal)到相应目录下,并输入以下命令查看id号:

ps xua

  由结果可知,一共有四个僵尸进程,第四个子进程被回收

2.waitpid() 与 wait() 的同效(阻塞):

#include"stdio.h"
#include"stdlib.h"
#include"unistd.h"
#include"sys/wait.h"
int main(){

        pid_t pid,wpid;
        printf("Begin...\n");
        int i = 0;
        for(i = 0;i < 5;i++){

                pid = fork();
                if(pid == -1){
                         perror("fork error!");
                         exit(1);
                 }
                 else if(pid == 0){
                         break;
                }
                else if(i == 3){
                        wpid = pid;
        }
        }
        if(i < 5){
                sleep(i);
                printf("i am %d child,pid = %u\n",i+1,getpid());
        }
        else{
                sleep(i);
                printf("i am parent!\n");
                while(waitpid(-1,NULL,0));      // 等价于 wait(NULL);
        }


        return 0;
}

在执行代码的过程中,开启另一个终端(terminal)到相应目录下,并输入以下命令查看id号:

ps xua

   由结果可知,此时已经没有僵尸进程;此外,补充的是,每次调用一次 wait(),则对一个子进程进行回收,因而使用 while循环 来去回收子进程,而 waitpid 的返回值 在简要介绍中有写,可以根据此来进行理解。

3.非阻塞状态下进行回收

#include"stdio.h"
#include"stdlib.h"
#include"unistd.h"
#include"sys/wait.h"
int main(){

        pid_t pid,wpid;
        printf("Begin...\n");
        int i = 0;
        for(i = 0;i < 5;i++){

                pid = fork();
                if(pid == -1){
                         perror("fork error!");
                         exit(1);
                 }
                 else if(pid == 0){
                         break;
                }
        }
        if(i < 5){
                sleep(i);
                printf("i am %d child,pid = %u\n",i+1,getpid());
        }
        else{
                sleep(i);
                printf("i am parent!\n");
                do{
                        wpid = waitpid(-1,NULL,WNOHANG);
                        if(wpid > 0)
                                i--;
                }while(i > 0);
                printf("finish\n");
        }

        
        return 0;
}

 执行完代码之后,输入以下命令查看:

ps xua

  由结果可知,子进程已经全部被回收;

  --- 其余命令涉及到进程组,我们暂且不对其进行讨论


参考资料:

1.B站---Linux全套教程


写在最后:

       该博客是本人学习的一些总结,如果各位有不同见解,可以评论提出或者与我联系改正,谢谢大家!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

S_h_a_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值