进程终止

进程退出

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
3
#include<stdlib.h>
int atexit(void (*func)(void));
//返回值:若成功,返回0;若出错,返回非0

​ 2) 冲刷并关闭所有处于打开状态的标准I/O流。

​ 3) 删除所有通过tempfile创建的临时文件。

​ 在这些事情完成后,调用_exit来做更深层次的清理,包括1) 关闭所有仍处于打开状态的文件描述符。2)将调用进程的子进程托付给init进程。3)向调用进程的父进程发送SIGCHLD信号。

4. 异常终止的讨论

​ 说完了正常终止的方式,我们来看看进程的异常终止。

​ 在很多时候,异常终止的原因都是因为信号的默认处理方式。默认的信号处理程序会根据具体信号涵义采取不同处理方式:

  • 终止进程运行,并且产生core dump文件。
  • 终止进程运行。
  • 忽略信号,进程继续执行。
  • 暂停进程执行。
  • 如果进程已经暂停,重新调度进程继续运行。

前两种便是我们所说的因为信号而产生的异常终止,但这很多时候并不是程序本身的问题。实际上是存在因为程序本身的问题导致异常终止的情况。

假设在访问内存时发生了非法访问的情况, 就会出发内存保护异常,并跳转到异常处理程序,终止进程运行。异常处理函数在实现上,是通过向挂起的进程发送信号,进而通过信号的默认处理程序来终止进程运行,在这信号发生前内核可能会试着修复,在发现无法挽回后,进程的死活便取决于信号的默认处理函数了。所以这种情况下的实际终止原因还是信号的处理。

关于调用abort异常终止也是通过给进程发送信号的原理,具体的abort源码可以了解下。