18 进程等待

目录

1.为什么进程等待
2.进程等待函数
3.子进程status
4.非阻塞等待

1. 为什么进程等待

1.子进程退出,父进程不管,就会形成僵尸进程,造成内存泄露
2.僵尸进程kill也无法杀死
3.父进程给子进程的任务完成情况需要知道,结果是否正常退出
4.父进程通过进程等待方式,回收子进程资源,获取退出信息

2. 进程等待函数

运行下面代码监测,会发现僵尸进程

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    pid_t id = fork();

    if (id < 0)
    {
        perror("fork");
        exit(1); //标识进程完毕,结果不正确
    }
    else if (id == 0)
    {
        //子进程
        int cnt = 5;
        while (cnt--)
        {
            printf("我是子进程: %d, %d\n", getpid(), getppid());
            sleep(1);
        }

        exit(0);
    }
    else 
    {
        //父进程
        while (1)
         {
        printf("我是父进程: %d %d\n",getpid(), getppid() );
            sleep(1);
        }
    }

    return 0;
}

父进程需要通过等待的方式来回收子进程的资源和结果,避免出现僵尸进程
查看等待函数的说明
在这里插入图片描述

wait方法

#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*status);
返回值:
成功返回被等待进程pid,失败返回-1。
参数:
输出型参数,获取子进程退出状态,不关心则可以设置成为NULL

将上面代码加上等待子进程,就会发现僵尸进程被回收了

waitpid

pid_ t waitpid(pid_t pid, int *status, int options);
返回值:
当正常返回的时候waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
参数:
pid:
Pid=-1,等待任一个子进程。与wait等效。
Pid>0.等待其进程ID与pid相等的子进程。
status:
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
options:
WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进
程的ID。

第一个参数pid>0时等待指定进程,-1等待任一子进程
option默认为0,表示阻塞等待
status输出型参数,会输出子进程的退出信息

  • 如果子进程已经退出,调用wait/waitpid时,会立即返回,并且释放资源,获得退出信息
  • 如果子进程正在运行,可能会阻塞,子进程退出后再继续执行下面的程序
  • 不存在子进程,立即出错返回

3. 子进程status

3.1 解析

这个输出型参数,由操作系统填充,传递为NULL,表示不关心退出信息。否则要提取需要的信息
在这里插入图片描述

3.2 手动获取

status的次8为退出状态,最后8位是终止信号
验证一下:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    pid_t id = fork();
    if (id < 0)
    {
        printf("创建失败\n");
        exit(0);
    }
    else if (id == 0)
    {
        //子进程
        int cnt = 5;
        while (cnt--)
        {
            printf("我是子进程,%d %d\n", getpid(),getppid());
            sleep(0.5);
        }
        exit(105);
    }
    else
    {
        //父进程
        int status = 0;
        pid_t ret = waitpid(id, &status, 0);
        printf("等待成功,返回值: %d ,退出信号: %d, 退出码: %d\n", ret, status & 0x7f, (status >> 8) & 0xff);
    }
    return 0;
}

通过提取最低7位可以得到子进程的退出码,表示程序结果正常。次8位是子进程的退出信号
在这里插入图片描述

退出信号是0,表示正常跑完。如果是明显有错误的会是什么信号,用一个野指针程序来观察
在这里插入图片描述
用kill -l 查询11信号
在这里插入图片描述

段错误信号,说明程序运行不正常。系统杀死程序也是通过发送信号的方式,9号信号

那么可以不可以用全局变量拿到子进程的退出结果。不可以,进程具有独立性,子进程会发生写时拷贝,无法拿到
进程的退出结果信息保存在task_struct结构中,拿到进程信息本质就是读取PCB信息里的内容,因为wait的函数本身就是系统调用,有这个权利

3.3 宏获取

系统提供了获取退出状态和退出码的宏,可以直接使用

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

int main()
{
    pid_t id = fork();
    if (id < 0)
    {
        printf("创建失败\n");
        exit(0);
    }
    else if (id == 0)
    {
        //子进程
        int cnt = 5;
        while (cnt--)
        {
            printf("我是子进程,%d %d\n", getpid(),getppid());
            sleep(0.5);
        }
        exit(105);
    }
    else
    {
        //父进程
        int status = 0;
        pid_t ret = waitpid(id, &status, 0);
        if (ret > 0)
        {
            if (WIFEXITED(status))
            {
                printf("正常退出,退出码: %d\n",WEXITSTATUS(status));
            }else 
            {
                printf("异常退出,退出码: %d\n",WEXITSTATUS(status));
            }
        }
    }
    return 0;
}

在这里插入图片描述

4. 非阻塞等待

前面的父进程等待时,不能做其他事情,只能子进程返回了才继续执行。这就是阻塞等待,系统上处于阻塞队列中,没有被cpu调度。父进程怎么在等待的同时执行其他的任务,这就是非阻塞等待

waitpid的第三个参数默认为0,表示阻塞等待,如果为1,就是非阻塞等待。不过系统定义了一个宏,WNOHANG,用这个表示非阻塞等待
在这里插入图片描述

阻塞和非阻塞:

父进程通过调用waitpid等待,如果子进程没有退出,waitpid这个系统调用,立马返回,这样就可以非阻塞

在这里插入图片描述
上面的是伪代码,非阻塞时传入的参数在系统调用中会判断,非阻塞就会直接返回,阻塞就会将父进程挂起,等子进程退出再唤起,开始返回

将父进程的代码修改一下,不断获取子进程的等待信息,输入代码测试:在这里插入图片描述

while (1)
        {
        //父进程
        int status = 0;
        pid_t ret = waitpid(id, &status, WNOHANG);
        if (ret > 0)
        {
           // if (WIFEXITED(status))
           // {
           //     printf("正常退出,退出码: %d\n",WEXITSTATUS(status));
           // }else 
           // {
           //     printf("异常退出,退出码: %d\n",WEXITSTATUS(status));
           // }
           printf("等待成功,退出\n");
           return 0;
        }
        else if (ret == 0)
        {
            printf("等待成功,子进程未退出\n");
        }
        else 
        {
            printf("等待失败\n");
        }
        sleep(1);
        } 
     }

非阻塞等待的情况下,可以让父进程同时执行一些其他事情,用一个函数指针数组保存两个函数,在等待的同时调用vector保存的这几个函数

#include <iostream>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <unistd.h>

typedef void (*handler_t)(); //函数指针类型

std::vector<handler_t> handlers; //函数指针数组

void fun_one()
{
    printf("这是一个临时任务1\n");
}
void fun_two()
{
    printf("这是一个临时任务2\n");
}

// 设置对应的方法回调
// 以后想让父进程闲了执行任何方法的时候,只要向Load里面注册,就可以让父进程执行对应的方法喽!
void Load()
{
    handlers.push_back(fun_one);
    handlers.push_back(fun_two);
}

int main()
{
    pid_t id = fork();
    if(id == 0)
    {
        // 子进程
        int cnt =  5;
        while(cnt)
        {
            printf("我是子进程: %d\n", cnt--);
            sleep(1);
        }

        exit(11); // 11 仅仅用来测试
    }
    else
    {
        int quit = 0;
        while(!quit)
        {
            int status = 0;
            pid_t res = waitpid(-1, &status, WNOHANG); //以非阻塞方式等待
            if(res > 0)
            {
                //等待成功 && 子进程退出
                printf("等待子进程退出成功, 退出码: %d\n", WEXITSTATUS(status));
                quit = 1;
            }
            else if( res == 0 )
            {
                //等待成功 && 但子进程并未退出
                printf("子进程还在运行中,暂时还没有退出,父进程可以在等一等, 处理一下其他事情??\n");
                if(handlers.empty()) Load();
                std:: vector<handler_t>::iterator it = handlers.begin();
                while (it != handlers.end())
                {
                    (*it)();
                    it++;
                }
            }
            else
            {
                //等待失败
                printf("wait失败!\n");
                quit = 1;
            }
            sleep(1);
        }
    }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值