面试题【2】‘exit()’与‘_exit()’的区别
调用 exit() 时,库层会执行以下步骤:
-
调用用户注册的清理函数
- 通过
atexit()注册的函数会按照逆序(后注册先执行)依次被调用。 - 这一步允许程序在正式退出前完成自定义的资源释放、日志写入等工作。
- 通过
-
刷新标准 I/O 缓冲区
exit()会遍历所有打开的FILE*流(包括stdin、stdout、stderr),如果流是 全缓冲(默认)或 行缓冲(在交互式终端上),则把缓冲区中的数据写回底层文件描述符。- 这一步确保在进程结束前,所有通过
printf、fprintf等函数产生的输出都已经落盘或发送到终端。
-
关闭打开的文件描述符
- 对每个打开的文件描述符调用
close(),触发文件系统层面的资源回收(如写入磁盘、释放锁等)。
- 对每个打开的文件描述符调用
-
向父进程发送退出状态
- 通过系统调用
exit_group(在 Linux 中)将进程的退出码status传递给父进程,以便wait()/waitpid()能够获取。
- 通过系统调用
-
销毁内核中的进程结构
- 内核回收进程的 PCB(进程控制块)、用户态的虚拟内存空间等。
正因为 exit() 在系统调用前加入了上述“包装”,它被视为 “不是纯粹的系统调用”,而是一套完整的用户态清理流程。
2. _exit() 的工作流程
_exit()(在 POSIX 标准中也称为 _Exit)定义在 <unistd.h>,原型如下: