一:进程退出函数详解
在前一篇的 Unix进程环境之---进程结束的方式和退出函数 中提到了Unix程序退出的八中方式,分别为:
1:从 main 函数返回
2:调用 exit 函数
3:调用 _exit 函数或者是 _Exit 函数
4:从最后一个线程中返回
5:从最后一个线程中调用 pthread_exit
还有三种非正常的结束方式:
6:调用 abort
7:接收到信号
8:应答最后一个线程的取消请求
以下就各个函数做比较详细的解释:
1:执行return从main函数返回等同于调用exit函数。
2:调用exit函数,这个函数有ISO C所定义,包括调用调用所有的被atexit注册过的退出处理程序和关闭所有的标准I/O流。因为ISO C不会处理文件描述符、多进程、作业控制,所以在Unix系统中,这个函数的定义有些不完整。
3:ISO C定义了一个_Exit函数来提供进程结束时,不用执行退出处理程序和信号处理 程序。在Unix系统中,_Exit和_exit同义,都不用 刷新标准I/O流 。 _exit函数由POSIX.1所定义。
在大多数 Unix系统中exit函数是标准的C库函数,_exit是系统调用。
4:线程的返回值不作为进程的返回值,当最后一个线程从例程返回时,进程结束时的结束状态值为0。
5:和4中的一样,这种情况下同样返回0,与传递到pthread_exit函数中的参数无关。
不管一个进程如何结束,内核都会为这个结束的进程关闭所有的打开的件描述符,释放所利用的内存,等等。
如果我们想要结束的进程通知父进程自己是如何结束的,对于exit 、 _Exit和_exit ,通过传递退出状态值,作为函数的参数来实现。
对于非正常的结束方式,有内核,而不是结束进程,生成一个结束状态值来指示非正常结束的原因。这时,父进程可以通过 wait和waitpid函数 来获取结束进程的结束状态值。
二:为什么任何一个进程都有父进程
当调用一个fork函数后,子进程就会有一个父进程。当父进程先于子进程结束时,init进程会成为这个子进程的父进程。
具体的实现如下,通常情况下,在一个进程结束时,内核会遍历所以的活动进程,看是否有结束进程的子进程。如果有这个进程Parent PID值,修改为1(init进程的PID),这样保证了所以的进程都有父进程。
三:僵死进程的产生
在子进程先于父进程结束的情况下,当父进程要检查一个子进程是否结束时,子进程完全消失了,这是父进程不能取得结束子进程的结束状态信息。
在一个进程结束时,内核会保存保存每个结束进程的少量信息。这些信息最少包含,进程ID,结束状态值,进程利用的CPU时间。
当父进程调用wait或者是waitpid时,可以利用这些结束信息。
僵死进程 产生的原因,在Unix系统术语中,如果一个进程结束了,而父进程没有等待他(wait),成为僵死进程。
如果我们写一个长时间运行的程序,在这个程序中fork了很多子进程,如果父进程没有wait和取得他们的结束状态值,那么他们会成为僵死进程。
如果一个进程继承了init,那么他是否会成为僵死进程,答案是否定的。因为init的任何一个子进程结束时,init会调用wait函数来取得结束状态值。从而避免僵死进程的产生。
从上可以看出,在 子进程先于父进程 结束,而父进程没有wait这个子进程才会产生僵死进程。