子进程的异步等待方式

我们知道当一个父进程创建一个子进程时,会调用wait和waitpid函数清理僵⼫进程,⽗进程可以阻塞等待⼦进程结束,也可以⾮阻塞地查询是否有⼦进程结束等待清理(也就是轮询的⽅式)。采⽤第⼀种⽅式,⽗进程阻塞了就不 能处理⾃⼰的⼯作了;采⽤第⼆种⽅式,⽗进程在处理⾃⼰的⼯作的同时还要记得时不时地轮询⼀ 下,程序实现复杂。

  • 1.wait和waitpid

(1)wait 函数:用来等待任何一个子进程退出,由父进程调用。

#include<sys/types.h> 
#include<sys/wait.h> 
pid_t wait(int* status);

返回值:成功返回被等待子进程的pid,失败返回-1。 status:输出型参 数,拿回子进程的退出信息。如果参数status不为空,则进程终止状态被 保存于其中。 wait方式:阻塞式等待,等待的子进程不退出时,父进程一直不退出。 目的:回收子进程,系统回收子进程的空间。

依据传统,返回的整形状态字是由实现定义的,其中有些位表示退出状态(正常返回),其他位表示信号编号(异常返回),有一位表示是否产生了一个core文件等。 终止状态是定义在 sys/wait.h中的各个宏,有四可互斥的宏可以用来取得进程终止的原因。

WIFEXITED:正常返回时为真,可以执行宏函数 WEXITSTATUS获取子进程传送给exit、_exit或_Exit的参数的低8位。

WIFSIGNALED:异常返回时为真,可以执行宏函数WTERMSIG取得子进程终止的信号编号,另外,对于一些实现,定义有宏WCOREDUMP宏,若以经昌盛终止进程的core文件,则为真。

WIFSTOPPED:若为当前暂停子进程的返回的状态,则为真,可执行WSTOPSIG取得使子进程暂停的信号编号。

WIFCONTINUED:若在作业控制暂停后已经继续的子进程返回了状态,则为真,仅用于waitpid。

  • (2)waitpid:
#include<sys/types.h> 
#include<sys/wait.h> 
pid_t waitpid(pid_t pid,int* status,int options);
  • 1>参数

从参数的名字pid和类型pid_t中就可以看出,这里需要的是一个进程ID。但当pid取不同的值时,在这里有不同的意义

pid>0:只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。

pid=-1:等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。

pid=0:等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。

pid<-1:等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。

status参数与wait函数的基本相同。

options参数当options参数为0时,与wait功能相同,仍是阻塞式等待,不提供额外功能,如果为下列常量按位或则提供更多功能:

WCONTINUED:若实现支持作业控制,那么由pid指定的任一子进程在暂停后已经继续,但状态尚未报告,则返回状态

WNOHANG:若由pid指定的子进程并不是立即可用的,则waitpid不阻塞,即此时以非阻塞方式(轮询式访问的必要条件)等待子进程,并且返回0。

WUNTRACED:若实现支持作业控制,而pid指定的任一子进程已经暂停,且其状态尚未报告,则返回其状态

  • 2>返回值:

当正常返回的时候,waitpid返回收集到的子进程的进程ID;

如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;

如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;

当pid所指示的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid就会出错返回,这时errno被设置为ECHILD;

  • 3>waitpid提供了三个wait所没有的功能:

    1. waitpid可等待一个特定的进程

    2. waitpid提供了一个wait的非阻塞版本

    3. waitpid支持作业控制

  • 2.关于SIGCHLD信号

其实,⼦进程在终⽌时会给⽗进程发SIGCHLD信号,该信号的默认处理动作是忽略,⽗进程可以⾃ 定义SIGCHLD信号的处理函数,这样⽗进程只需专⼼处理⾃⼰的⼯作,不必关⼼⼦进程了,⼦进程 终⽌时会通知⽗进程,⽗进程在信号处理函数中调⽤wait清理⼦进程即可。

一般情况下父进程收到这个信号的默认处理是忽略这个信号,即就是不做任何处理,但是我们可以通过系统调用API:signal来进行自定义处理句柄,进行验证,具体代码如下:

(1)验证子进程退出时会给父进程发送SIGCHLD信号

void catshsig(int sig)
{
    printf("get s sig: %d pid: %d\n", sig, getpid());
}
int main(void)
{
    signal(SIGCHLD, catchsig);
    pid_t id = fork();
    if(0 == id) //child 
    {
        printf("I am child, qiut! pid: %d\n", getpid());
        exit(1);
    }
    else
    {
        waitpid(id, NULL, 0);
        printf("father is waiting...\n");
    }
}

程序完成以下功能:⽗进程fork出⼦进程,⼦进程调⽤exit(1) 终⽌,⽗进程⾃定义SIGCHLD信号的处理函数,在其中调⽤wait获得⼦进程的退出状态并打印。

用kill -l指令查看17号信号:

kill -l 查看17号信息

  • 3.子进程的异步等待方式

(1)异步回收僵尸进程:

fork之后,非阻塞(异步)等待子进程(回收僵尸)。

fork之后,子进程和父进程分叉执行,僵尸进程的产生是因为父进程没有给子进程“收尸”造成的,又可以根据危害程度分为下述两类:

总体来说:当子进程结束之后,但父进程未结束之前,子进程将成为僵尸进程。

  • 当子进程结束之后,但父进程未结束之前,子进程将成为僵尸进程,父进程结束后僵尸被init进程回收。
  • 如果子进程结束了,但是父进程始终没有结束,那么这个僵尸将一直存在,而且随着exec,僵尸越来越多。

    (2)代码实现

#include<stdio.h> 
#include<stdlib.h>
#include<signal.h> 
#include<unistd.h> 
#include<sys/types.h> 
#include<sys/wait.h> 
    void catchSig(int sig) 
    { 
        printf("father is catching,child is quit\n"); 
 //以非阻塞方式等待所有异常退出的子进程 
 pid_t id; 
    while((id = waitpid(-1,NULL,WNOHANG)) > 0) 
    { 
        printf("wait child success:%d\n",id); 
    } 
 } 
    int main 
    { 
        signal(SIGCHLD,catchSig); 
        pid_t pid1 = fork; 
        if(pid1 == 0)//child1 
        { 
            printf("child1:my pid is %d\n",getpid); 
            exit(-1);//子进程1异常终止 
        } 
        pid_t pid2 = fork;//child2 
        if(pid2 == 0) 
        { 
            printf("child2:my pid is %d\n",getpid); 
            exit(-1);//子进程2异常退出 
        } 
        pid_t pid3 = fork;//child3 
        if(pid3 == 0) 
        { 
            printf("child3:my pid is %d\n",getpid);//子进程3正常运行  
        } 
        while(1) 
        { 
            printf("father is waiting...\n"); 
            sleep(1); 
        } 
        return 0;

运行结果:
运行结果

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值