1,进程终止的8种方式
5种正常方式:
- 从main函数返回;
- 调用exit;
- 调用_exit或_Exit;
- 最后一个线程从其启动例程返回;
- 从最后一个线程调用pthread_exit
3种异常方式
- 调用abort;
- 接到一个信号;
- 最后一个线程对取消请求做出响应
其中,_exit与_Exit是立即进入内核,而exit是先执行一些清理处理(包括执行注册的终止处理程序、关闭打开的流),然后再执行_exit()返回内核。
整个过程如下:
2,终止状态
通常我们在main函数结束的时候,都会写上return 0,或者return 1.这个0或者1就是终止状态。
一个程序执行后的终止状态是可以查询到的
echo $?
打印的就是程序执行后的状态。
3,atexit
前面提到在调用exit的时候,会先执行“终止处理程序”(exit handler)。这些终止处理程序又是如何指定的呢?
我们可以通过调用atexit函数来注册终止处理程序。一个进程可以注册多至32个这样的函数。
函数原型:
#include <stdlib.h>
int atexit(void (*func)(void));
注册成功返回0.
一个使用的例子如下:
#include <stdio.h>
#include <stdlib.h>
// 声明终止处理程序
static void my_exit1(void);
static void my_exit2(void);
//main函数
int main(){
// 注册 exit handler
if(atexit(my_exit2) != 0){
printf("can't register exit2\n");
}
if(atexit(my_exit1) != 0){
printf("can't register exit1\n");
}
if(atexit(my_exit1) != 0){
printf("can't register exit1\n");
}
//主函数的逻辑
printf("hello, world\n");
return 0;
}
//定义终止处理程序
static void my_exit1(void){
printf("%s\n",__func__);
}
static void my_exit2(void){
printf("%s\n",__func__);
}
最终的执行结果如下:
可以得出如下结论:
- 终止处理程序的调用顺序与它们的注册顺序正好相反;比如我们是先注册my_exit2,后注册my_exit1,但执行起来刚好相反。
- 同一函数如果被注册多次也就能被调用多次,比如我们的my_exit1注册了两次也就被执行了两次。
- atexit()函数的输入参数类型是void (*func)(void),这就意味着我们的终止处理程序(exit handle)都是没有输入参数的,正如我们的my_exit1和my_exit2那样。想想也对,因为这些函数都是被自动调用的,如果有输入参数,要如何把参数传递给他们呢?!