目录
4.解释一下errno与waitpid输中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 进程返回与信号与运行结果的关系