进程等待与退出

目录

前言

0.进程如何退出

1.进程status的获取

2.进程status的组成

3.退出status的分用与验证

4.解释一下errno与waitpid输中status参数的关系

5.status设计


前言

我们在编写程序的时候,我们往往需要知道这个程序中的某个进程执行情况是怎么样的,在Linux平台下有"退出码"、"退出信号"来描述进程退出的结果,退出的结果会返回给该进程的父进程,对于云服务器来说实际上就是将我们搭载主函数的进程返回bash进程,那么对于我们主函数中创建的一级子进程,进程的结果自然就会返回给我们的主函数。本文要讨论的就是进程如何返回给父进程退出结果,以及推出结果有什么作用。


0.进程如何退出

一个进程有多种退出方式(部分):

①进程运行出错,被强制终止

②对进程发送信号,让进程终止

③使用exit()函数,进程返回结束

④return,函数返回结束

注意:进程一旦退出就一定有对应的退出码(与主函数的return返回值类似)

1.进程status的获取

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

    if(id==0)
    {
        exit(1);
    }
    int status;
    pid_t rid=waitpid(id,&status,0);
    std::cout<<"status: "<<status<<std::endl;
    return 0;
}

在这个示例代码中我们使用fork创建一个子进程并让其使用exit(),让子进程返回,其中返回值为1

pid_t waitpid(pid_t id,int &status,int option)
//等待成功返回等待进程的pid,
传入三个参数,
第一个参数表示要等待进程的id,
第二个参数是一个输出型参数,该参数会获得等待进程的进程退出状态码
第三个参数是关于如何等待的选择,0表示阻塞等待。

图1 程序运行结果

2.进程status的组成

图2 status数据分布

进程status是一个32位的整数,其中这个整数的高16位不被使用全部被设置为0。对于该整数的低16位,其中的高8位用来存储进程的返回值,就是我们上文说到的使用exit()函数返回的值。其中的低8位,其中的低7位用来储存该进程接收到的信号,低8位的最高位用来存储core dump表示位。

在子进程没有接收到信号的时候,低16位中的高8位被允许使用。当子进程受到来自操作系统的信号的时候,高8位的内容就会失效。

core dump标识位(在博主的信号讲解部分还会在提到):这个标识位表示是否在文件异常或出错的时候,是否生成核心转储文件(core dump file)。这个文件中的内容可以帮助程序员对错误和异常进行分析,但是这个文件中可能包含用户的登入信息,会造成用户信息泄露,所以通常情况这个表示位是关闭的。即此bit位为0(本文默认关闭,即此bit为0)。

3.退出status的分用与验证

在第2部分中我们介绍了进程status的数据组成,本本分来进行验证。 该部分分为两部分进行验证。

先让子进程使用exit()函数进行退出,此时代码可以在不发送信号的情况下正常退出,所以信号接收部分为0,同时我们没有开启core dump标识位,所以整体低八位应该为0。

而后验证,发送信号到进程对进程status码低7位的影响。验证此部分,将子进程中注释的死循环打开,在该死循环中,我们获取子进程的pid,有了子进程的pid我们就可以向该进程发送信号。编译运行程序,我们这里假设向子进程发送9号信号(该信号可以强制终止进程)。

#include <iostream>
#include <unistd.h>
#include <sys/wait.h>

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

    if(id==0)
    {
        // while(1)
        // {
        //     std::cout<<"i am childpid , my pid is "<<getpid()<<std::endl;
        // }
        exit(1);
    }
    int status;
    pid_t rid=waitpid(id,&status,0);
    std::cout<<"status: "<<status<<std::endl;

    //打印高八位内容
    std::cout<<"high_eight_bits: "<<((status>>8)&0xff)<<std::endl;
    
    //打印低八位内容
    std::cout<<"low_eight_bits: "<<(status&0xff)<<std::endl;
    
    //打印第八位中的最高位内容
    std::cout<<"highest_low_eight_bit: "<<((status>>7)&1)<<std::endl;

    return 0;
}

图3 对进程退出码高八位进行验证
图4 对进程发送信号验证其退出码中的低7位信息

对于图3结果,可做如下解释:
图5 对图3、图4结果解释

注意:这里进程的返回为什么使用exit(),而不使用return,这是因为,return是对函数的值返回,假如子进程调用了其他函数,其他函数可通过return返回一个值,但是子进程不会直接退出执行流。但是使用exit(),即使是exit()在被调用的函数中,也会立即令进程返回。当然exit与return在本部分所展示的代码中的子进程返回表现没有差异。

4.解释一下errno与waitpid输中status参数的关系

先说结论:二者没有任何关系

errno是在进程执行库函数和系统调用的时候被设置的,用来表明程序中的错误,本质上是一个与进程同级的全局变量,可以用来进行错误提示、区分错误类型、跨函数传递错误信息。

status是属于进程的一个变量,创建在父进程中,数据来源于子进程,由操作系统进行字段的填充与组合。本质上是一种进程间通信的手段,可以让父进程获取子进程的退出状态。

5.status设计

子进程的等待值实际上是由程序员和操作系统共同维护的,当在进程中出现一些操作系统可容许的错误时,这个时候进程不会崩溃,也就是我们常说的程序运行时错误,比如缺少某些资源,但是不足以让程序崩溃,这个时候子进程的返回值主要由子进程的返回值来控制,程序员在编写代码时,可自行编写一份错误手册,并按照一定的编码方式将这个值返回,而后又操作系统写入status中。但,当进程发生操作系统不可容忍的操作时,比如除0错误、越权访问等,这个时候操作系统会立即出手并向进程发送信号,对进程进行处理,此时程序员设置的错误信息无效,子进程的返回值由操作系统主导。

按照图2中status的表现形式,我们可以做如下推断:
图6 进程返回与信号与运行结果的关系

相关推荐
王哲晓29 分钟前
Linux通过yum安装Docker
java·linux·docker
gopher951143 分钟前
linux驱动开发-中断子系统
linux·运维·驱动开发
码哝小鱼1 小时前
firewalld封禁IP或IP段
linux·网络
鼠鼠龙年发大财1 小时前
【x**3专享】安装SSH、XFTP、XShell、ARM Linux
linux·arm开发·ssh
nfgo1 小时前
快速体验Linux发行版:DistroSea详解与操作指南
linux·ubuntu·centos
Rookie_explorers2 小时前
Linux下go环境安装、环境配置并执行第一个go程序
linux·运维·golang
weixin_424215842 小时前
shell运算实战案例-KFC点餐系统
linux·centos
小黑爱编程3 小时前
【LInux】HTTPS是如何实现安全传输的
linux·安全·https
BeyondESH3 小时前
Linux线程同步—竞态条件和互斥锁(C语言)
linux·服务器·c++
鱼饼6号3 小时前
Prometheus 上手指南
linux·运维·centos·prometheus