【Linux进程】进程退出

目录

前言

1. 进程退出的几种情况

2. 进程常见的退出方式

3. 退出码与错误码

4. 进程异常

 5. exit与_exit

 6. 进程等待

wait与waitpid

 获取子进程status

非阻塞等待


前言

        进程执行结束退出,就必然需要进行资源回收,子进程由父进程回收,而父进程(起始进程)被操作系统回收,那么当进程退出时,可能需要知道进程的执行情况,是正常执行完毕退出,还是异常退出?本文我们就来聊一聊进程的退出;

在这里插入图片描述

1. 进程退出的几种情况

  • 程序运行完毕,结果正确
  • 程序运行完毕,结果不正确
  • 程序异常终止

2. 进程常见的退出方式

正常终止:

  • main函数返回
  • 调用exit
  • _exit

异常终止:Ctrl + C (信号终止);

3. 退出码与错误码

        错误码:错误码通常用来衡量库函数或系统调用的执行情况,当函数调用出现问题的时候,可以返回一个特定的错误码;

输出错误码对应的信息;

int i = 0;
for (i = 0; i < 200; i++)
{
    printf("%d: %s\n", i, strerror(i));
}

 退出码:退出码表示的是进程退出时,它的退出结果;

进程是正常退出?还是异常退出?

正常退出:main函数执行完毕返回0,调用进程退出的接口(exit,_exit)

异常退出:一般都是收到信号退出;

 echo $? 查看最近一次进程执行完毕的退出码;

他们的共同点:用来衡量进程、函数出错时的详细信息;

思考:

在执行程序时,为什么main函数返回值是0 ?

回头再来思考这个问题,想必大家的心中已然有了答案;

main函数的返回属于是正常的进程退出,main函数的返回值,就是进程的退出码,0->success, !0->Failed;

4. 进程异常

        运行程序时,程序崩溃,可以说就是进程异常,这时就只能由OS来干掉进程,如何杀死进程?发送信号;

进程异常终止,本质其实就是进程收到了信号,自己终止了;

所以判断一个进程是否异常,只需要看它是否收到信号

         在跑一个程序时,判断代码是否执行成功,就可以根据退出码来判断;而对于父进程,只需要关注两个数字:信号、退出码就可以判断子进程运行结果怎么样;

 5. exit与_exit

共同点:exit和_exit的都是让进程退出;

不同点:

  • exit是库函数,_exit是系统调用;
  • exit退出时会刷新缓冲区,_exit退出不会刷新缓冲区;

 

exit是基于系统调用_exit实现的 ;

为什么?

        缓冲区的概念只存在于C库中,从操作系统视角来看其实并没有什么所谓的缓冲区;这也就是解释了exit会刷新缓冲区,而_exit不会刷新的原因;

 6. 进程等待

        进程等待就是父进程对子进程进行资源回收等待的过程;

        子进程由父进程创建,用来执行任务,子进程就必然会分配资源,子进程执行完毕那就需要父进程对子进程进行资源回收;

拓展

进程调用fork创建进程,当控制转移到内核中的fork代码后,内核做:

  • 分配新的内存块和内核数据结构给子进程
  • 将父进程部分数据结构内容拷贝至子进程
  • 添加子进程到系统进程列表当中
  • fork返回,开始调度器调度

 为什么需要等待?

  •  解决子进程变成僵尸进程所带来的内存泄露的问题;
  • 父进程创建子进程完成任务,子进程执行的如何?父进程是需要知道的,进程等待就可以获取到子进程执行退出的情况

 子进程退出情况不是必须的,但有的时候需要,所以系统提供了这样的功能;

wait与waitpid

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: 输出型参数,输出子进程退出状态,传NULL表示不关心子进程状态

 使用示例:

int status = 0;
pid_t rid = waitpid(id, &status, WNOHANG); // 0为阻塞等待
  •  进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。
  • 在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。
  • 如果不存在该子进程,则立即出错返回

 获取子进程status

status占32位bit位(32位环境下)这里主要讨论的是低的16位

 

 0~15位用来记录进程状态(正常终止或异常终止);

我们可以通过位运算来提取:

(status >> 8) & 0xFF //提取退出码(正常退出情况)
status & 0x7F // 提取收到的信号(异常退出)

 若0~7位为0则表明是正常退出,否则就为异常退出;

 系统也提供了接口便于我们获取:

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

 使用示例:

if( WIFEXITED(status) && ret == pid )
{
    printf("child return code is :%d.\n",WEXITSTATUS(status));
}

 思考:为什么不选择使用全局变量来获取子进程的状态?

        答案其实在前边的文章就就已经有了解释,进程具有独立性,子进程在修改数据时会发生写时拷贝,子进程对数据的修改父进程是不可见的,所以父进程是无法之间获取子进程的退出信息的;

非阻塞等待

        等待方式:waitpid的第三个参数;

        为0则表示阻塞式等待,WNOHANG:非阻塞等待

while(1){
    int status = 0;
    pid_t rid = waitpid(id, &status, WNOHANG);
    if (rid > 0)
    {
            printf("child quit success, exit code: %d, exit signal: %d \n", (status >> 8) & 0xFF, status & 0x7F);
            break;
    }
    else if (rid == 0)
    {
         printf("father do other thing...\n");
         // 父进程执行的任务
     
    }
    else
    {
        printf("wait failed!\n");
    }
}
  •  rid > 0 等待成功
  • rid == 0 等待成功,但子进程没有退出;
  • rid < 0 等待失败

父进程在等待的过程中,可以做一些自己占时间不多的任务;

选择非阻塞等待往往需要进行重复的调用,轮询 + 非阻塞方案;上述就是一个简单的示例;


        以上便是本文的全部内容,希望对你有所帮助或启发,感谢阅读!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值