今天仔细看了一下exit和_exit这两个函数的区别,实际上exit也是调用了_exit退出函数的,只不过在调用_exit之前,exit还进行了一些多余的工作,也正是因为这样,相比起来exit就没有那么接近底层的系统调用,更应该说是包装过的标准C库函数。_exit包含在头文件unistd.h中,exit包含在头文件stdlib.h中,我们来查看一下他们的函数原型。
man _exit :
很显然,_exit函数做这三件事情:
1. 让调用的进程马上终止。
2.关闭所有由这个进程打开的文件描述符。
3.调用进程的所有子进程都被初始化init进程收养,调用进程将发送SIGCHLD给他的父进程(这都是因为他即将要退出了,当然要安顿好自己的孩子和告别父母啦)。
man exit:
exit则做了这几件事:
1.按axexit或者on_exit注册时相反的顺序调用所有由它注册的函数(出口函数),可以把on_exit看作atexit的扩展。(这使得我们可以指定在程序终止时执行自己的清理动作.例如,保存程序状态信息于某个文件,解开对共享数据库上的锁等。)如果有任意一个注册的函数不返回(比如这个函数call _exit或者用像SIGKILL这样的信号把自己干掉了),那么剩余的注册函数都不会得到执行,而且接下来更深层次的exit都不会被执行。如果一个函数被注册了多遍,那么也会按顺序执行多遍(这些是atexit的特性)。
2.所有打开的输入输出流都将被清空和关闭,换句话说就是把缓冲区的内容写回文件中。那些用tmpfile函数创建的临时文件都将被移除。
3.调用_exit。
所以,总的来说,exit就是对_exit进行了一些包装,使得整个退出的过程显得不那么粗暴,他们俩的共同点就是都会关闭文件描述符,都会清空内存,但是exit还会额外地清空输入输出流缓存,移除临时创建的文件,调用注册好的出口函数等等。
现在我们只需要用一个小例子来观察他们俩的区别就可以了:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> int main(int argc, char const *argv[])
{
printf("hello\n");
printf(" hi");
exit(0);
//_exit(0);
return 0;
}
对于上面这段代码,我们的执行结果为:
而我们把代码改一改:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> int main(int argc, char const *argv[])
{
printf("hello\n");
printf(" hi");
//exit(0);
_exit(0);
return 0;
}
结果就变成了:
这显然是因为_exit并没有进行缓冲区的清空等操作,而exit则会把缓冲的内容都清空。
参考博客: