分类: 系统运维
2012-03-29 12:46:07
有8种方法终止一个进程。普通终止有5种:
1、从main函数中返回;
2、调用exit;
3、调用_exit或_Exit;
4、最后线程从start函数返回;(11.5节)
5、从最后线程里调用pthread_exit(11.5节)
异常终止有3种:
6、调用abort(10.17节)
7、收到一个信号(10.2节)
8、最后线程回应一个取消请求(11.5节和12.7节)
目前,我们将忽略这三个与线程相关的终止方法,直到我们在11章和12章讨论线程。
我们在前一节提到的启动程序通常被写为:当main函数返回时,exit函数被调用。如果启动程序用C编码(它通常用汇编来编码),main函数的调用可能看起来像:
exit(main(argc, argv));
Exit 函数
三个普通终止程序的函数:_exit和_Exit从内核立即返回;eixt执行特定清理处理然后从内核返回。
我们将在8.5节讨论这三个函数在其它进程,比如终止进程的子进程和父进程,上的效果。
不同头文件的原因是exit和_Exit由ISO C规定而_exit由POSIX.1规定。
历史上,exit函数问题执行一个标准I/O库的清理:fclose函数被调用来关闭所有打开的流。回想下5.5节,这会使所有缓冲的输出数据被冲洗(写入文件)。
这三个exit函数都期望一个整型参数,我们称它为退出状态。多数UNIX系统的外壳提供一种检查进程退出状态的方法。如果a、这些函数中任何一个被调用
而没有退出状态,b、main返回而没有返回值,或者c、main函数没有被声明为返回一个整型值,那么进程的退出状态是无定义的。然而,如果main的
返回类型是一个整型数而main从函数掉出来(一个隐含的return),那么进程的退出状态为0。
这是在ISO C标准的1999版本的新行为。历史上,如果main函数到达结束位置,而没有显式调用一个return语句或exit函数,退出状态是无定义的。
从main函数里返回一个整型值等价于用相同值调用exit。因而exit(0);和main函数里的return(0);相同。
看下经典的hello world程序:
$ cc hello_world.c
$ ./a.out
hello, world
$ echo $?
13
启用1999 ISO C编译扩展:
$ cc -std=c99 hello_world.c
hello_world.c:3:1: warning: return type defaults to ‘int’
$ ./a.out
hello, world
$ echo $?
0
注意当雇用1999 ISO
C扩展时编译器警告出现。这是因为main函数的返回值没有显式声明为一个整型。如果我们加上这个声明,那这个消息就不会出现。然而,如果我们启用所有推
荐的错误(用-Wall标志),那么我们将会看到类似于“control reaches end of nonvoid
function.”的消息。(我在gcc version 4.5.2 (Ubuntu/Linaro
4.5.2-8ubuntu4)上没有看到这个消息。)
main的作为整型返回的声明和使用exit而不是return从一些编译器和lint程序产生不需要的警告。问题是编译器不知道从main函数的
exit和一个return是相同的。一种处理这些警告的方式,在一段时间后会变得很烦,是在main里用return而不是exit。但这样做会阻止我
们使用UNIX系统的grep工具来定位一个程序里的所有的exit调用。另一个解决办法是把main声明为void,而不是int,然后继续调用
exit。这处理了编译器警告,但看起来不对(特别是在编程文本里),而且能产生其它的编译错误,因为main的返回值应该是一个有符号的整型。在本文,
我们让main返回一个整型,因为这是ISO C和POSIX.1共同定义的。
不同的编译器的警告有不同的信息。注意GNU C编译器通常不产生这些不重要的警告信息,除非额外的警告选项被指定。
atexit函数
在ISO C,一个进程可以最多注册32个自动被exit函数调用的函数。这些被称为exit处理器,并通过调用atexit来注册。
声明说我们传递一个函数地址作为atexit的参数。当这个函数被调用时,不传入任何参数也不返回任何值。exit函数以它们注册的顺序的相反顺序调用这些函数。每个函数都被调用和它被注册的一样多的次数。
这些退出处理器第一次出现在1989年的ANSI C标准里。在ANSI C之前的系统,比如SVR3和4.3BSD,没有提供这些退出处理器。
ISO C要求系统支持至少32个退出处理器。sysconf函数可能用来决定一个给定系统支持的退出处理器的最大数量。
在ISO
C和POSIX.1,exit首先调用退出处理器,然后(通过fclose)关闭所有打开的文件,最后调用_exit或_Exit回到内核。
POSIX.1扩展了ISO C标准,指出如果程序调用任何一个exec家族的函数,那么任何安装的退出处理器将被清理掉。
注意内核执行一个程序的唯一方法是调用一个exec函数。进程自愿终止的唯一方法是调用_exit或_Eixt,不管是显式地还是隐式地(通过调用exit函数)。一个进程也可以被一个信号非自愿地终止。
下面的代码使用了atexit函数: