文章目录
- 进程控制
- 1、进程终止
- 1.1进程常见退出方法
- 退出码
- 1.1.1 strerror函数 & errno宏
- 1.1.1 _exit函数
- _exit和exit的区别
- 结合现象分析:
进程控制
1、进程终止
1.1进程常见退出方法
进程退出场景
- 代码运行完毕,结果正确
- 代码运行完毕,结果不正确
- 代码异常终止
正常终止(可以通过echo $?
查看进程退出码)
1.从
main
返回2.调用exit
3._exit
异常退出:
- ctrl + c,信号终止
退出码
当异常退出时,可以用return的不同的返回数字,表征不同的出错原因,即退出码
在正常写一个C/C++程序的时候,经常使用return 0;
,但是在多进程的环境中,return的值就有讲究了。
1.1.1 strerror函数 & errno宏
strerror
头文件:#include<string.h>
返回值:指向描述error errnum
的错误字符串的指针,简单来说可以将退出码和对应的错误对应上。
举例:
#include<string.h>
int main()
{
for(int i=0;i<10;i++){
printf("%d: %s\n",strerror(i));
}
return 0;
}
效果如下,后面输出的就是退出码对应的错误描述:
errno
头文件:#include<errno.h>
简单的说,errno会返回最后的一次错误码,使用errno可以获得退出码,通过返回退出码,在多进程中也可以让父进程知道子进程的状况。
注意:但是当进程异常退出的时候,本质可能就是代码没有跑完,那么进程的退出码就无意义了,所以应该要先看进程退出的时候,如果要关心进程的推出情况,要先关心退出时后有没有出异常,如果没有异常,再看结果是否正确,然后关心退出码。
- 父进程关心子进程的退出,只需要确定:
- 父进程是否收到来自子进程的信号,若没有,说明没有异常,代码正常跑完
- 查看退出结果:0表示成功,非0表示错误,对应各自的原因
举例:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{
int ret = 0;
char *p = (char*)malloc(1000*1000*1000*4);
if(p==NULL){
printf("malloc error, %d: %s\n".errno,strerror(errno));
ret = errno;
}
else{
printf("malloc success\n");
}
return ret;
}
1.1.1 _exit函数
头文件:#include<unistd.h>
函数格式:void _exit(int status);
_exit函数最后也会调用exit,但是在调用exit之前,还会做以下工作:
1、执行用户通过
atexit
或on_exit
定义的清理函数2、关闭所有打开的流,所有的缓存数据均被写入
3、调用_exit
例1:
int main()
{
printf("hello linux\n");
exit(12);
}
或者:
int main()
{
printf("hello linux\n");
return 12;
}
编译执行完后再使用echo $?
查询退出码,效果均如下:
区别在于:exit在任意地方被调用,都表示调用进程直接退出,如果调用的是return,只表示当前函数返回,原进程继续运行,如果调用一个含exit或者return的函数,就可以明显观察到。
_exit和exit的区别
结合现象分析:
int main()
{
printf("hello world");
sleep(1);//使用sleep能够观察到一些现象,下文会提及
exit(11);
}
运行完毕后再调用echo $?
查看退出码,效果如下:
但是将exit
改为_exit
后:
int main()
{
printf("hello world");
sleep(1);
exit(11);
}
运行完毕后再调用echo $?
查看退出码,效果如下:
原因:
当代码中输出的内容以\n结尾时,当代码运行到printf这条语句时,程序会直接输出内容,但是如果没有以\n结尾,那么就会先将内容存到缓冲区中,当程序结束前会冲刷缓冲,关闭流,然后就有打印输出的效果,也正因此会发现运行的时候是先等待了一秒钟,输出句子后程序马上结束,而不是先输出句子,等待一秒钟再结束程序。
结合下图
- 调用
exit()
后会先执行用户定义的清理函数,再冲刷缓冲,关闭流等,因此会有打印字符串的效果,最后再调用_exit系统调用_exit()
是一个系统调用接口,调用_exit()
后,其会在操作系统内部直接终止进程,对应缓冲区的数据不做刷新。