【Linux进程特别篇】深度理解辨识僵尸进程和孤儿进程
---------------------------------------------------------------------------------------------------------------------------------
每日鸡汤:每一份坚持都是成功的积累,只要相信自己,总会遇到惊喜。
-------------------------------------------------------------------------------------------------------------------------------
目录
编辑
一:进程的状态
二:僵尸进程
2.1:僵尸进程的定义与特点
2.2:僵尸进程怎样产生的,如何避免出现僵尸进程?
三:孤儿进程
2.1:孤儿进程的定义与特点
2.2:孤儿进程怎样产生的,如何避免出现孤儿进程?
四:区分辨识僵尸进程和孤儿进程
4.1:僵尸进程和孤儿进程的实例源码
4.1.1:创建僵尸进程
4.1.2:创建孤儿进程
4.2:总结区别僵尸进程和孤儿进程
一:进程的状态
进程在操作系统中可以处于多种状态,这些状态反映了进程在生命周期中的不同阶段。通常,最基本的进程状态模型包括三种状态:就绪(Ready)、运行(Running)和阻塞(Blocked),有时也称为等待(Waiting),但是在Linux中还有一些其他的进程状态,比如暂停态,僵尸态,孤儿态等
即,僵尸进程和孤儿进程是Unix/Linux操作系统中两种特殊类型的进程状态。它们都涉及到父进程与子进程之间的关系 。
为了深度理解父子进程,了解僵尸进程和孤儿进程是学习道路上必不可少的一个阶段。
二:僵尸进程
2.1:僵尸进程的定义与特点
僵尸进程的定义:
僵尸进程是一个已经完成执行(即其所有代码都已运行完毕),但其父进程尚未读取其退出状态的进程。
僵尸进程的特点:
- 僵尸进程不再占用任何CPU资源或内存资源,但它仍然在系统进程表中占有一项,保留了一些信息(如进程ID、退出状态等)。
- 如果不及时处理僵尸进程,系统中的进程表会逐渐被填满,最终可能导致无法创建新的进程。
2.2:僵尸进程怎样产生的,如何避免出现僵尸进程?
僵尸进程由很多的危害,那么僵尸进程是如何产生的呢
当一个子进程终止时,它会向其父进程发送一个信号(通常是 SIGCHLD),通知父进程它的退出状态。如果父进程没有调用 wait() 或 waitpid() 来读取子进程的退出状态,子进程就会变成僵尸进程。
避免出现僵尸进程:
父进程尽快通过wait()
或waitpid()
来收集子进程的状态信息,这样内核就可以回收子进程的PCB。如果父进程不这样做,可以考虑发送信号给父进程强制其处理,或者重启父进程。
三:孤儿进程
2.1:孤儿进程的定义与特点
孤儿进程的定义:
如果一个进程的父进程在它之前终止了,而该进程自己仍然存活并运行着,这样的进程被称为孤儿进程。
孤儿进程的特点:
- 在现代Unix/Linux系统中,一旦某个进程成为孤儿进程,init进程(进程号为1)会自动收养这些孤儿进程。
- init进程会负责等待孤儿进程结束,并且清理它们的状态信息。
- 孤儿进程继续正常运行,直到它们自然终止。
2.2:孤儿进程怎样产生的,如何避免出现孤儿进程?
孤儿进程的产生:
- 父进程在子进程之前退出:当一个进程(父进程)创建了一个或多个子进程后,如果父进程在它的所有子进程之前终止了,那么这些子进程就变成了孤儿进程。这是因为每个进程都有一个父进程,而一旦父进程不再存在,子进程就会失去它们的父进程。
- 父进程被强制终止:有时候,父进程可能因为某种原因被系统管理员或者通过其他程序(如使用kill命令)强制终止。如果此时有活动的子进程,那么这些子进程也会变成孤儿进程。
- 父进程崩溃:如果父进程由于编程错误或者其他问题而崩溃,同样会导致其子进程成为孤儿进程。
- 父进程主动放弃子进程:在某些情况下,父进程可能会调用特定的函数(例如prctl(PR_SET_PDEATHSIG, SIGKILL))来设置自己死亡时向子进程发送信号,从而让子进程知道自己的父进程已经不存在,并采取相应的行动。但如果不这样做,子进程仍会成为孤儿
避免出现孤儿进程:
通常不需要特别处理孤儿进程,因为init进程会接管它们。但如果希望避免产生孤儿进程,可以在编写程序时确保父进程妥善地管理其子进程的生命周期,例如使用信号处理器来捕获父进程的退出信号并在退出前清理子进程。
四:区分辨识僵尸进程和孤儿进程
4.1:僵尸进程和孤儿进程的实例源码
4.1.1:创建僵尸进程
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>int main()
{pid_t id = fork();if(id == 0){//子进程int cnt = 5;while(cnt){printf("我是子进程,pid: %d, ppid: %d, cnt: %d\n",getpid(),getppid(),cnt);cnt--;sleep(1);}exit(0);}else{while(1){printf("我是父进程,pid: %d, ppid: %d\n",getpid(),getppid());sleep(1);}}return 0;
}
父进程还没有运行完,而子进程运行完了,子进程的资源没有被父进程回收。
查看实例情况:
4.1.2:创建孤儿进程
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>int main()
{pid_t id = fork();if(id == 0){//子进程int cnt = 500;while(cnt){printf("我是子进程,pid: %d, ppid: %d, cnt: %d\n",getpid(),getppid(),cnt);cnt--;sleep(1);}exit(0);}else{int cnt1 = 5;while(cnt1--){printf("我是父进程,pid: %d, ppid: %d, cnt1 = %d\n",getpid(),getppid(), cnt1);sleep(1);}}return 0;
}
当父进程运行完毕回收,而子进程却还正在运行。此时该子进程就被称为孤儿进程。因为孤儿进程的父进程已经被回收了,所以为了保障到时候该子进程运行结束有父进程回收他的资源(不让他成为僵尸进程),操作系统(init进程,进程号为1)会 “收养” 该子进程,所以该子进程的父进程就是init进程,其父进程号为1,那么该子进程就被称为孤儿进程。
查看实例情况:
4.2:总结区别僵尸进程和孤儿进程
子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入僵尸状态,子进程就称之为“僵尸进程”
父进程先退出,子进程还在运行,子进程被操作系统“领养”,子进程就称之为“孤儿进程”