让进程终止有 8 种方式,其中 5 种为正常终止,分别为:
(1)从 main 返回;
(2)调用 exit;
(3)调用 _exit 或 _Exit;
(4)最后一个线程从其启动例程返回;
(5)从最后一个线程调用 pthread_exit。
异常终止有 3 种方式,分别是:
(1)调用 abort;
(2)接到一个信号;
(3)最后一个线程对取消请求做出响应。
启动例程就是这样编写的:从 main 返回后立即调用 exit 函数。如果将其以 C 代码形式表示(实际上常常用汇编语言编写),则它调用 main 函数的形式可能是:
exit(main(argc, argv));
下面 3 个函数用于正常终止一个程序。
#include <stdlib.h> void exit(int status); void _Exit(int status); #include <unistd.h> void _exit(int status);
其中 _exit 和 _Exit 会立即进入内核,exit 则总是执行一个标准 I/O 库的清理关闭操作后才返回内核。这三个函数所带的整型参数常被称为终止状态。大多数 UNIX 系统 shell 都提供检查进程终止状态的方法。如果(a)调用这些函数时不带终止状态,或(b)main 执行了一个无返回值的 return 语句,或(c)main 没有声明返回类型为整型,则该进程的中指状态就是未定义的。而若 main 的返回类型是整型,并且 main 最后返回(隐式返回),那么该进程的终止状态就是 0。
按照 ISO C 的规定,一个进程可调用 atexit 函数登记多个终止处理程序函数(可以使用 sysconf 函数来确定),这些函数将由 exit 以与登记时相反的顺序自动调用。同一函数如若登记多次,也会被调用多次。
#include <stdlib.h> int atexit(void (*func)(void)); /* 返回值:若成功,返回 0;否则,返回非 0 */
exit 首先调用各终止处理程序,然后使用 fclose 关闭所有打开流。如若程序调用 exec 函数族中的任一函数,则将清除所有已安装的终止程序。
下图显示了一个 C 程序是如何启动的,以及它终止的各种方式。
注意,内核使程序执行的唯一方法是调用一个 exec 函数,自愿终止的唯一方法是显示或使用 exit 隐式地调用 _exit 或 _Exit。进程也可非自愿地由一个信号来终止(图中未显示)。
下面是一个 atexit 函数使用的示例。
#include <stdio.h> #include <stdlib.h> static void my_exit1(void); static void my_exit2(void); int main(void){ if(atexit(my_exit2) != 0) printf("can't register my_exit2\n"); if(atexit(my_exit1) != 0) printf("can't register my_exit1\n"); if(atexit(my_exit1) != 0) printf("can't register my_exit1\n"); printf("main is done\n"); return 0; } static void my_exit1(void){ printf("first exit handler\n"); } static void my_exit2(void){ printf("second exit handler\n"); }
编译后的运行结果:
$ ./atexitDemo.out main is done first exit handler first exit handler second exit handler