进程退出
1. 正常方式
- 从main返回
- 调用exit
- 调用_exit或_Exit
- 最后一个线程从其启动例程返回
- 最后一个线程调用pthread_exit
2. 异常终止
- 调用abort
- 接到一个信号
- 最后一个线程对取消请求作出相应(待补充)
3. 正常结束的善后内容
我们先来看看exit和_exit与_Exit区别,首先exit和_Exit是ISO定义的函数,定义在<stdlib.h>头文件中,而_exit则是基于POSIX标准,属于系统调用,定义在<unistd.h>头文件中。这三个函数的参数都带一个整型参数,表示进程的退出状态。如果a) 调用这些函数时不带参数,或b) main执行了一个无返回值的return语句,或c)main没有声明返回类型为整型,则返回状态是未定义的。但是若main的返回类型是整形,并且main执行到最后一条语句时返回,那么进程的终止状态是0。exit(0)表示正常退出,其他情况都表示异常退出。
_exit其实是exit调用过程的一个子集,首先exit退出进程前,会进行一系列的善后工作,包括1) 调用通过atexit或on_exit注册的退出处理函数。
1 |
|
2) 冲刷并关闭所有处于打开状态的标准I/O流。
3) 删除所有通过tempfile创建的临时文件。
在这些事情完成后,调用_exit来做更深层次的清理,包括1) 关闭所有仍处于打开状态的文件描述符。2)将调用进程的子进程托付给init进程。3)向调用进程的父进程发送SIGCHLD信号。
4. 异常终止的讨论
说完了正常终止的方式,我们来看看进程的异常终止。
在很多时候,异常终止的原因都是因为信号的默认处理方式。默认的信号处理程序会根据具体信号涵义采取不同处理方式:
- 终止进程运行,并且产生core dump文件。
- 终止进程运行。
- 忽略信号,进程继续执行。
- 暂停进程执行。
- 如果进程已经暂停,重新调度进程继续运行。
前两种便是我们所说的因为信号而产生的异常终止,但这很多时候并不是程序本身的问题。实际上是存在因为程序本身的问题导致异常终止的情况。
假设在访问内存时发生了非法访问的情况, 就会出发内存保护异常,并跳转到异常处理程序,终止进程运行。异常处理函数在实现上,是通过向挂起的进程发送信号,进而通过信号的默认处理程序来终止进程运行,在这信号发生前内核可能会试着修复,在发现无法挽回后,进程的死活便取决于信号的默认处理函数了。所以这种情况下的实际终止原因还是信号的处理。
关于调用abort异常终止也是通过给进程发送信号的原理,具体的abort源码可以了解下。