分类:
2011-12-29 07:31:46
原文地址:Linux进程-进程的退出 作者:ddbddb1
void _exit(int status)
注意点:
1.status表明了进程终止时的状态。当子进程使用_exit()后,父进程如果在用wait()等待子进程,那么wait()将会返回status状态,注意只有status的低8位(0~255)会返回给父进程。通常,我们使用0,表示进程成功返回,非负值表示进程不成功返回。但是,这种约定不是强制的,每个应用程序都可以自己指定。
2.虽然返回值可以是0~255,但是我们通常使用0~128。因为在shell编程中,如果一个进程被信号打断,shell会返回128+信号编号。在shell中,这两个值是没有区别的(都是当做进程返回值),如果我们进程中使用了128~255,那么就无法区别到底是信号打断还是进程自己退出了。
void exit(int status)
注意点:
1.在exit()调用后,退出处理函数(exit handler)首先被执行(这些函数使用atexit()和on_exit()注册)。
2.stdio流缓冲区被刷新。(还有其他资源的清理)
3.最后一步才是调用_exit()退出。
4.在main函数中最后调用return n和调用exit()是相同的作用。如果在main函数最后调用return或不调用任何退出函数,调用main的运行时函数会自动的调用exit(),但是status会变得不确定(在C89中,status可能会是栈中的某个值,或CPU寄存器中的某个值,这要看编译器是怎么使用的。在C99中,要求status必须是0)。
注册退出处理函数
在程序退出之前,我们都会主动地做一些清理的动作,比如把程序缓冲区的数据保存在文件上。通常的做法是在exit()之前,调用一些清理函数。但这种做法的弊端是,在每个可能退出的地方都写上一大堆相同的代码。另外一种方式就是让程序中所有可能退出的点,都全部集中在一个地方处理。这样做的问题是会出现大量的判断是否成功语句,让代码显得不清晰。Linux提供了下面的两个函数来解决这种问题,把所有的清理函数注册进lib库中,当任意一处调用exit()时,系统会自动的调用注册上的清理函数。
int atexit(void (*func)(void))
注意点:
1.可以注册多个函数,并且一个函数也可多次注册。当要调用它们时,是按照FILO顺序执行的。
2.函数注册后,没办法取消注册。
3.清理函数中调用exit()的行为,在SUSv3中没有定义。在linux中,会继续执行剩下的清理函数。但在某些系统中,可能出现死循环。
4.子进程会继承父进程中的清理函数。
#define _BSD_SOURCE
int on_exit(void (*func)(int, void *), void *arg)
注意点:
1. on_exit()是atexit()的改良版。在on_exit()中,可以通过func中的int参数知道exit()的退出状态;同一个func可以根据arg的不同,执行不同的代码。
2.唯一的缺点是并不是每个系统都实行了改函数。
代码分析
当在终端执行该程序时,输出入下:
Hello World
CC
当把文字输出到文件时,文件中保存的文字如下:
CC
Hello World
Hello World
分析:当把文字输出到终端时,stdio函数组是按行输出。而输出到文件时,是按照块输出。也就是说,在输出到文件时,printf()函数把”Hello World\n”输入进用户stdio缓冲区,但是没有达到输出块的大小。write函数直接把“CC\n”输出到内核缓冲区,而不是用户进程的缓冲区。接下来fork()出一个子进程,子进程的用户缓冲区是拷贝父进程的,也就是说在子进程中的stdio缓冲区中也存在一个”Hello World\n”,但是内核缓冲区是不会拷贝的。CC出现在Hello World前面是因为执行完write()后,CC已近在内核缓冲区中,而printf()后“Hello World”还在用户stdio缓冲区中,直到执行了exit(),才把“Hello World”刷新到内核缓冲区中。