目录
进程退出
退出场景
常见的退出方法
正常退出
异常退出
_exit()系统调用
exit()函数
_exit()和exit()的区别
return
进程退出
退出场景
- 正常符合预期退出
- 正常不符合预期退出
- 异常退出(执行过程中异常奔溃, 还未执行完)
常见的退出方法
正常退出
- main函数返回 ( return )
- 调用 exit( int status )函数
- 使用 _exit( int status )系统调用接口
可以使用ench $? 来查看进程退出码
异常退出
- 向进程发送信号导致进程异常退出(如 Ctrl+c)
- 代码错误导致进程运行时奔溃异常退出
说明 : 第二种情况是代码错误导致异常终止没什么说的, 就是代码问题
第一种情况是 Unix / Linux 系统中的信号是系统响应某些状况而产生的事件,是进程间通信的一种方式。信号可以由一个进程发
送给另外进程,也可以由核发送给进程 .
信号处理程序
信号处理程序是进程在接收到信号后, 系统对信号的响应. 根据具体信号的涵义, 相应的默认信号处理程序会采取不同的信号处理方式:
- 终止进程运行, 并且产生core dump (核心转储文件)(记录一些错误信息, 方便用户查看)
- 终止进程运行
- 忽略信号,进程继续执行 .
- 暂停进程运行 .
- 如果进程已被暂停,重新调度进程继续执行 .
前两种方式会导致进程异常退出. 实际上,大多数默认信号处理程序都会终止进程的运行。
在进程接收到信号后,如果进程已经绑定自定义的信号处理程序, 进程会在用户态执行自定义的信号处理程序. 如果没有绑定,内
核会执行默认信号程序终止进程运行, 导致进程异常退出 .
例如: kill()函数, 在Shell中执行kill 指令, 在终端用键盘发送信号, 如:Ctrl+c , 都是发送信号来终止进程
_exit()系统调用
void _exit (int status)
头文件 : unistd.h
参数 :status 定义了进程的终止状态,父进程通过wait() 来获取该值(wait() 函数, 用于进程等待, 下面说).
说明 :虽然status是int,占但是仅有低8位可以被父进程所用. 在下面小结进程等待中详细说
功能 : 直接使进程停止运行,清除其使用的内存空间,并销毁其在内核中的各种数据结构
exit()函数
void exit (int status)
头文件 : stdlib.h
参数status 与_exit中同理
exit() 底层封装了 _exit 系统调用, 在底层调用_exit之前, 还做了下面的工作
- 执行用户通过 atexit 或 on_exit定义的清理函数。
- 关闭所有打开的流,所有的缓存数据均被写入 (即刷新缓冲区)
- 调用 _exit()
_exit()和exit()的区别
- 最大的区别是 exit()函数在调用 _exit() 系统调用之前要检查文件的打开情况,把文件缓冲区中的内容写回文件 (即刷新缓冲
区), 然后将控制权交给内核 . _exit() 则是执行后立即返回给内核,而exit()要先执行一些清除操作,
- 调用_exit函数时,会关闭进程所有的文件描述符,清理内存以及其他一些内核清理函数,但不会刷新缓冲区, exit函数是在_exit函数之上的一个封装,其会调用_exit,并在调用之前先刷新缓冲区。
- 补充: exit()函数在调用exit系统之前要检查文件的打开情况,把文件缓冲区的内容写回文件。由于Linux的标准函数库中,由于内存中都有一片缓冲区. 每次读文件时,会连续的读出若干条记录,这样在下次读文件时就可以直接从内存的缓冲区读取. 同样,每次写文件的时候也仅仅是写入内存的缓冲区,等满足了一定的条件(如达到了一定数量或遇到特定字符等),再将缓冲区中的内容一次性写入文件。这种技术大大增加了文件读写的速度,但也给编程代来了一点儿麻烦。比如有一些数据,认为已经写入了文件,实际上因为没有满足特定的条件,它们还只是保存在缓冲区内,这时用_exit()函数直接将进程关闭,缓冲区的数据就会丢失。因此,要想保证数据的完整性,就一定要使用exit()函数。
举栗子
#include<stdio.h>
#include<stdlib.h>
int main(){
printf("hello world!");
exit(0);
return 0;
}
#include<stdio.h>
#include<unistd.h>
int main(){
printf("hello world!");
_exit(0);
}
可以看到, 并没有输出hello world! , 这就_exit()没有刷新缓冲区, 导致在缓冲区的字符串没有打印到显示器进程就退出了, 造成数据丢失 .(注意hello world! 后面不能有\n, 因为\n会刷新输出缓冲区, 影响结果)
return
return是一种更常见的退出进程方法. 执行return n等同于执行exit(n), 因为main中 return n时, 系统会将main的返回值当做 exit()
的参数