exit()和_exit()的效果都是让程序退出执行,而_exit()用来“尽快”退出。
atexit()
先说一下atexit()函数。我们可以用atexit()注册一个或多个函数退出清理函数(或者on_exit()但这个函数不建议用),这些清理函数按照注册时的反顺序,在exit()或main函数return时被调用。
#include <stdlib.h>
int atexit(void (*function)(void)); //return 0 on success.
注意,fork子进程时,这些函数会被继承到子进程。而exec系列执行成功后,所有函数会被清空。
exit()
我们知道父进程要wait子进程的退出状态,在子进程退出到父进程调用wait()期间,子进程就处于僵尸状态。因此,exit()将进程正常退出,并将(status & 0377)返回到父进程的wait(),其中status可以是EXIT_SUCCESS或EXIT_FAILURE。
#include <stdlib.h>
void exit(int status);
exit()在返回到父进程前要做的事情包括:按出栈顺序(反序)依次调用atexit()/on_exit()注册的函数。然后将所有打开的stdio流进行flush并关闭,如果有通过tmpfile()创建的文件也会被删除。(这里要注意,如果你注册的某个清理函数中调用_exit()或把自己kill结果退出了,那么后面的清理函数以及刷stdio等就不会执行了。所以,清理函数里不要调用exit()或_exit()。)
子进程exit()后会发送一个SIGCHLD信号给父进程(如果父进程设置了SA_NOCLDWAIT,那这个信号是否发送是未定义的)。
如果子进程在exit()后,父进程已经在等待(wait()系列函数)子进程的状态,或者父进程设置了SA_NOCLDWAIT或者将SIGCHLD的处理置为SIG_IGN,子进程都会立即退出。而如果父进程既没有wait,又没有设置忽略子进程退出,子进程就会变成僵尸进程(除了一个字节的exit status,什么都没有,用来确保以后某个时刻父进程wait的时候仍能拿到status,来解除子进程的僵尸状态)。如果父进程到死都没有wait,那僵尸进程会被init收养然后用wait清理掉它。
_exit()
而_exit()是企图让程序“立即”退出,它不会调用上述atexit()/on_exit()注册的函数。它也是会关闭自己打开的所有文件描述符的,但是否flush stdio以及是否删除tmpfile创建的文件则是与具体实现相关的(即没有明确规定)。我本地实验的结果是exit()会flush所有打开文件(stdio和其他文件)的缓冲,而_exit()不会。