内核要运行一个应用程序,唯一的途径是通过系统调用。exec函数。exec又会调用启动程序,启动程序(一般是汇编语言)以类似以下的方式调用main函数:
void exit(main(argc, argv));
那么在main函数末尾使用exit(0)和使用return 0是等价的。
这里有三个正常终止程序的函数:
void exit(int status); // 先运行一些清理操作,然后进入内核
void _Exit(int status); // 马上进入内核,可在全部函数中调用
void _exit(int status); // 马上进入内核。可在全部函数中调用
exit函数一般做三类事情:
- 运行由atexit函数登记的各终止处理程序
- 总是运行一个标准IO库的清理关闭操作:为全部打开流调用fclose函数
- 调用_exit或_Exit函数返回内核
注意最后一步。exit终于还是要调用_exit或_Exit回到内核。
进程有5种正常终止方式和3种异常终止方式(P178),无论进程怎样终止,最后都会运行内核中的同一段代码。这段代码为对应进程关闭全部开打描写叙述符,释放它所使用的存储器等。
以下是一个进程启动和退出的总体流程图:
从上图能够看出,内核启动程序的唯一方法是exec系统调用,而进程自愿终止(相对于非自愿终止,如信号)的唯一方法是通过exit或_exit或_Exit函数回到内核。
注意到上面三个函数的參数status。这是进程退出时的
退出状态。内核将退出状态转换成
终止状态。该终止状态可以由该进程的父进程通过wait或waitpid函数捕获。也就是说父进程可以获得子进程退出时的终止状态。
依据上面的描写叙述。有几个问题须要考虑:
- 父进程在子进程之前终止,会发生什么?这样的情况下,它们的父进程会改变为init进程,称由init进程领养。
- 子进程在父进程之前终止,内核为终止子进程保存的信息怎样给父进程?在这样的情况下。父进程调用wait或waitpid能够得到这些信息。假设父进程没有进行这样的调用,则子进程占用的资源不会全然释放,这时该子进程称为僵死进程。
- 由init领养的进程终止时会发生什么?init被编写成不管何时,仅仅要有子进程终止。它都会调用wait函数获得其终止状态。
所以init的子进程不可能成为僵死进程。
參考:
《unix环境高级编程》 P147-P151、P178.