• 进程管理之wait和waitpid


    僵尸进程

    在介绍wait、waitpid和waitid函数之前,首先要介绍一下僵尸进程,因为,这三个函数的本质任务就是处理僵尸进程的问题。

    进程会我们的生命体一样,也有消亡。进程在退出时,内核会清理进程几乎所有的资源。例如:内存资源、文件资源、信号量资源、共享内存资源或者引用数减一 又或释放共享内存资源。但还有少量的资源没有被内核清理,例如:进程控制块PCB task_struct、内核栈资源。这些资源没有被释放,是为了保留一些进程退出是的重要信息,例如:进程消耗的系统cpu时间、用户cpu时间;收到了多少信号等待这些信息,类似于“墓志铭”,总结了进程的一生。而wait、waitpid和waitid函数就是来释放这些“墓志铭”信息的。之后进程就脱离了僵尸进程的状态。

    进程的僵尸进程的状态,是一种“刀枪不入的状态”,即使是用kill -9 也无法杀死。只能通过wait这些函数活着是通过init进程来“收尸”;

    对于创建了很多的子进程的父进程,获取子进程的退出信息是非常有意义的。

    wait函数

    函数声明

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

    返回值

    • 返回退出子进程的pid;
    • 等待过程中,收到了信号。信号打断了系统调用,并且注册信号处理函数时没有设置SA_RESTAR标识位,系统调用不会被重启。wait函数返回-1,全局变量error=EINIR(表示函数被信号中断);
    • 所有需要等待的子进程都已经退出,没有要等的子进程的。wait函数返回-1,全局变量error=ECHLD(表示调用进程时发现并没有子进程需要等待);

    由上述返回值的含义,等待所有子进程的退出时,要注意不要丢掉对信号中断情况的考虑,代码如下:

    pit_t my_wait(int *state)
    {
       int retval;
       while( (retval=wait(state))!=-1 && (error==EINIR) );
       return retval;
    }
    

    参数说明

    wait函数的参数和waitpid函数的参数是一个意思。但waitpid函数对wait函数的局限性做了扩展,所以,在介绍waitpid时,再来讲wait的参数。

    wait函数的局限性

    • 不能等待特定的子进程
    • 如果子进程不存在,就会阻塞
    • 只能探知子进程的死亡,却不能探测子进程的暂停,也无法探知进程的苏醒

    waitpid函数

    waitpid函数的声明

    #include<sys/wait.h>
    pid_t waitpid(pit_t pid,int *status,int options);
    

    返回值

    返回值和wait函数一样。

    参数说明

    pid参数

    • pid > 0:表示等待进程ID为pid的子进程;
    • pid = 0:表示等待与调用进程同一进程组的任意子进程;
    • pid = -1:表示等待任意子进程;
    • pid < -1:表示等待所有子进程中,进程组ID与pid绝对值相等的所有子进程;

    pid参数的理解:

       首先给父进程要等待的子进程分类。子进程分为:和父进程同一进程组的子进程,和父进程不同进程组的子进程。子进程可以设置自己的进程组,所以某些子进程不一定和父进程归属于同一个进程组;pid>0,很自然,代表要等待的子进程pid,这叫做“精准打击”;还有情况就是分类等待子进程。由上述分类可知,与父进程为同一进程组的子进程怎么表示,调用waitpid函数的父进程本来就知道自己的进程组pid,所以,不用设置参数pid的值,即给0就行;那与父进程不在同一进程组的子进程的话,就得设置参数pid的值了,但要和“精准打击”的方式区别开,所以给负值,但取绝对值;当然,等待任意子进程的需求还是有的,而现在也正好只剩下一个值“-1”,刚好给它用;综述,通过上面的分析才有了pid参数的使用方式。
    

    内核实现简述

    内核之中,wait和waitpid函数调用的都是wait4函数。根据pid的值来给wait_ opts结构体变量wo中的wo_type复制,再以实参的形式传参给do_wait函数,do_wait函数来决定等待什么状态的子进程。

    wait4中的部分代码:

    struct wait_opts wo;
            .
            .     //给type复制的过程
            .
    wo.wo_type = type;
    wo.wo_pid  = pid;
           .
           .
           .
    ret = do_wait(&wo);
    

    在do_ wait函数中,主要要完成两个人物,第一,父进程中的每个线程都会去遍历子进程,第二,筛选要等待的子进程;在内核中,task_struct成员中children变量是保存子进程链表的链表头,利用list_for_each_enpty函数来遍历。利用eligible_pid函数来筛选。

    遍历子进程代码:

    static int do_wait_thread(struct wait_opts *wo,
                                   struct task_struct *tsk)
    {
        struct task_struct *p;
        list_for_each_enpty(p,&tsk->children,silbling)
        {
            int ret = wait_consider_task(wo,0,p);
            if(ret)
                return ret;
        }
        return 0;
    }
    

    筛选子进程的代码:

    static int eligible_pid(struct wait_opts *wo,
                                 struct task_struct *p)
    {
        return wo->wo_type == PIDTYPE_MAX || 
               task_pid_type(p,wo->wo_type) == wo->wo_pid;
    }
    

    当waitpid函数参数pid的值为-1时,wo_type的值为PIDTYPE_MAX;其他三种情况由task_pid_type函数来处理。

    参数options

    是一个位掩码,可以同时存在多个标志。当options的值为0时,行为和wait类似。

    标志位:

    • WUNTRACE:关心终止子进程和因信号中断的子进程的信息(阻塞);
    • WCONTINUED:关系终止子进程和信号终止后由恢复执行的子进程(阻塞);
    • WNOHANG:指定的子进程没有发生变化,waitpid立即返回,返回值为0;出错返回时,返回值为-1,通过error=ECHILD和返回值来区分这两种情况。注意:error的值不会为EINTR,因为信号中断由WUTRACE来控制。

    参数status

    该参数存储的信息时按位存储的,我们没办法解析status的值,只能通过系统提供的宏去解析。这些宏安功能分可以分为两类:获取子进程状态和判断是非由相应信号产生;

    • 进程正常退出
    	WIFEIXITED(status):正常退出,返回true;
    	WEXITSTATUS(status):正常退出,获取进程退出状态;
    
    • 进程收到信号退出
      WIFSIGNALED(status): 被信号杀死,返回true;
      WTREMSIG(status):被信号杀死,返回杀死进程的pid;
      WCOREDUMOP(status):子进程产生core dump,返回true;
    
    • 进程收到信号停止
      WIFSTOPPED(status):收到相关信号,暂停执行,返回true;
      WSTOPSIG(status):如果子进程处于停止状态,该宏返回导致子进
      程停止的信号值;
    
    • 进程收到信号回复执行
    WIFCONTINUED(status): 递送SIGCONT信号,子进程回复执行,返回true;
    

    没有必要返回使子进程苏醒的信号的值,因为只有唯一一个SIGCONT信号才能使进程又停止状态恢复到执行状态。

  • 相关阅读:
    IOS开发-UIDynamic(物理仿真)简单使用
    IOS开发---视频录制
    利用阿里云服务器免费体验word press博客、个人网站
    Next Cloud通过修改数据库表,达到替换文件而不改变分享的链接地址的效果,以及自定义分享链接地址
    非华为笔记本如何实现多屏协同和一碰互传以及一些问题的解决方法
    如何申请XShell和XFtp的免费家庭学生版本
    PicGo配合Typora怎么配置Chevereto图床,PicGo的Chevereto图床配置
    Ubuntu无法正常引导,进不去Ubuntu,安装Ubuntu20和Window10双系统后,
    搭建自己的Chevereto免费图床—写博客更加得心应手了!
    如何搭建自己的本地服务器,Web服务器
  • 原文地址:https://www.cnblogs.com/cjaaron/p/7430048.html
Copyright © 2020-2023  润新知