进程退出场景
- 代码运行完毕,结果正确
- 代码运行完毕,结果不正确
- 代码异常终止
正常终止(可以通过 echo $? 查看进程退出码)
- 从 main 中 return 返回
- 调用exit:exit(0)表示正常退出,exit(非零)表示异常退出,功能是关闭所有文件终止正在执行的进程;
- _exit:直接使进程终止运行,清除其使用的内存空间,并销毁其在内核中的各种数据结构
异常退出:ctrl + c,信号终止
exit 和 return 的区别
https://blog.csdn.net/lb543210/article/details/81435694
https://www.cnblogs.com/noble/p/4144166.html
https://www.cnblogs.com/ECJTUACM-873284962/p/6882448.html
https://blog.csdn.net/headool/article/details/8018288
exit() VS _exit() 的区别?
exit()函数定义在stdlib.h中,而_exit()定义在unistd.h中
_exit()函数的作用最为简单:直接使进程停止运行,清除其使用的内存空间,并销毁其在内核中的各种数据结构;exit() 函数则在这些基础上作了一些包装,在执行退出之前加了若干道工序,也是因为这个原因,有些人认为exit已经不能算是纯粹的系统调用。
exit()函数与_exit()函数最大的区别就在于 exit()函数在调用exit系统调用之前要检查文件的打开情况,把文件缓冲区中的内容写回文件,防止丢失(清理I/O缓冲)
2、exit()在结束调用它的进程之前,要进行如下步骤:
- 执行用户自定义的清理函数:调用atexit()注册的函数(出口函数);按ATEXIT注册时相反的顺序调用所有由它注册的函数,这使得我们可以指定在程序终止时执行自己的清理动作.例如,保存程序状态信息于某个文件,解开对共享数据库上的锁等.
- 冲刷缓冲区,关闭流:cleanup();关闭所有打开的流,这将导致写所有被缓冲的输出,删除用TMPFILE函数建立的所有临时文件.
- 调用_exit()函数终止进程
在Linux的标准函数库中,有一套称作”高级I/O”的函数,我们熟知的printf()、fopen()、fread()、fwrite()都在此 列,它们也被称作”缓冲I/O(buffered I/O)”,其特征是对应每一个打开的文件,在内存中都有一片缓冲区,每次读文件时,会多读出若干条记录,这样下次读文件时就可以直接从内存的缓冲区中读取,每次写文件的时候,也仅仅是写入内存中的缓冲区,等满足了一定的条件(达到一定数量,或遇到特定字符,如换行符和文件结束符EOF), 再将缓冲区中的 内容一次性写入文件,这样就大大增加了文件读写的速度,但也为我们编程带来了一点点麻烦。如果有一些数据,我们认为已经写入了文件,实际上因为没有满足特定的条件,它们还只是保存在缓冲区内,这时我们用 _exit()函数直接将进程关闭,缓冲区中的数据就会丢失,反之,如果想保证数据的完整性,就一定要使用exit()函数。简单的说:
- exit函数将终止调用进程,在退出程序之前,所有文件关闭,缓冲输出内容将刷新定义,并调用所有已刷新的“出口函数”(由atexit定义)
- _exit:该函数是由操作系统定义定义的,在 UNIX 系统中不会 flush 标准I/O流; _exit终止调用进程,但不关闭文件,不清除输出缓存,也不调用出口函数
共同点:
不管进程是如何终止的,内核都会关闭进程打开的所有文件描述符(file descriptors)释放进程使用的内存空间(memory)!
问:为何在一个fork的子进程分支中使用_exit函数而不使用exit函数?
答:在由 fork() 创建的子进程分支里,正常情况下使用‘exit()’是不正确的。
- 这是因为使用它会导致标准输入输出的缓冲区被清空两次,造成临时文件删除;
- 在C++程序中情况会更糟,因为静态目标(static objects)的析构函数可以被错误地执行。
下面是一个程序启动和退出的流程;我们可以看到内核调用exec 启动C程序,调用main函数 ,用户函数,在这过程中,如果调用 _exit 会直接回到内核中,但是调用exit函数则会发生终止处理程序 和标准IO的清理,除非再一次调用 _exit 则退回到内核中
【图来自网络】