僵尸进程的分析

本文详细介绍了Linux环境下僵尸进程的产生原因及避免方法。包括僵尸进程的基本概念、产生的条件、实例演示及如何通过正确处理SIGCHLD信号来避免僵尸进程。

僵尸进程产生

 Linux进程退出并不代表被删除

在Linux环境下,当某个进程退出时并不代表该进程在系统中已经被删除,还有一些包含进程状态等数据保存在系统中,若不人为进行删除,则会一直囤积在系统中,久而久之就会产生大量剩余数据。

僵尸进程基本概念

Linux系统中,出了Init进程,其他所有进程都是通过parent进行fork出来的,我们成为child进程,当child进程退出后,内核中包含child进程的数据没有被删除,这种情况就是僵尸进程。一般情况下,每个进程在退出之后,都会发送一个SIGCHLD信号给它的parent进程,默认情况下,parent进程是不处理该信号事件。

僵尸进程产生的条件

当child进程退出时候,系统会发送一个SIGCHLD信号给它对应的parent进程,parent进行若不处理该信号,则会产生僵尸进程现象,若通过调用wait() 或者waitpid()函数则会处理该事件,就不会产生僵尸进程。

对于child进程和parent进程来说,有两种情景,就是谁先退出,若parent进程先退出,则child进程会被托付给Init进程,Init进程则有对SIGCHLD信号的处理,不会产生僵尸进程;若child进程先退出,parent进程一直没有退出,也没有执行对SIGCHLD信号处理操作,则会产生僵尸进程。

僵尸进程产生实例

我们利用ps命令可以查看僵尸进程,一般具有标志:


#include <stdio.h>
#include <stdlib.h>
int main(){
	pid_t pid,pr;
	pid = fork();
	switch(pid){

		case -1:
		printf("error");
		exit(EXIT_FAILURE);

		case 0:
                printf("child process\n");
		break;

		default:
			printf("parent pid = %d\n",getpid());
			while(1)
				sleep(2);
	}
	exit(0);
}

 上面实例中,由于parent进程一直处于运行状态,child进程退出时候,parent并没有调用wait() 或者waitpid() 函数操作,就会产生僵尸进程。

运行该应用后,执行ps命令:


qq@qq-Aspire-TC-606:~/qqzhong/tmp/c$ ps auxw|grep zombie
qq   4100  0.0  0.0   4196   356 pts/1    S+   16:41   0:00 ./zombie_tmp
qq   4101  0.0  0.0      0     0 pts/1    Z+   16:41   0:00 [zombie_tmp] <defunct>

如何避免僵尸进程

fork  和 wait 成对出现

上面我们可以看到,产生僵尸进程的条件,为了避免产生僵尸进程,必须是吸纳fork 和 wait成对出现,wait在fork之后进行调用。

避免产生僵尸进程实例

根据上面的实例,我们增加处理操作,代码如下:

#include <stdio.h>
#include <stdlib.h>
int main(){
	pid_t pid,pr;
	pid = fork();
	switch(pid){

		case -1:
		printf("error");
		exit(EXIT_FAILURE);

		case 0:
                printf("child process\n");
		break;

		default:
			printf("parent pid = %d\n",getpid());
			while(pr = wait(NULL) !=-1)
				;
			while(1)
				sleep(2);
	}
	exit(0);
}

运行该应用,通过ps命令查看后如下

qq@qq-Aspire-TC-606:~/qqzhong/tmp/c$ ps auxw|grep zombie
qq   4147  0.0  0.0   4196   356 pts/1    S+   16:45   0:00 ./zombie_tmp




### 处理和终止僵尸进程的方法 在Linux系统中,僵尸进程是已经结束但其退出状态尚未被父进程收集的子进程。这种状态会导致进程表中的资源未被释放,从而引发潜在的资源泄漏问题。以下是一些处理和终止僵尸进程的有效方法。 #### 1. 使用 `wait()` 或 `waitpid()` 系统调用 父进程可以通过调用 `wait()` 或 `waitpid()` 函数来收集子进程的退出状态信息,从而清除僵尸进程[^2]。在实际编程中,确保父进程正确实现这一机制是避免僵尸进程的关键。 ```c #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> int main() { pid_t pid = fork(); if (pid == 0) { // 子进程执行任务 _exit(0); } else { // 父进程等待子进程结束 waitpid(pid, NULL, 0); } return 0; } ``` #### 2. 使用信号处理机制 如果父进程未能主动调用 `wait()` 或 `waitpid()`,可以考虑通过信号处理机制捕获 `SIGCHLD` 信号并清理子进程的状态。这种方法可以在父进程中自动处理僵尸进程的问题[^4]。 ```c #include <signal.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> void handler(int sig) { while (waitpid(-1, NULL, WNOHANG) > 0); } int main() { signal(SIGCHLD, handler); pid_t pid = fork(); if (pid == 0) { // 子进程执行任务 _exit(0); } return 0; } ``` #### 3. 杀死父进程以释放僵尸进程 如果僵尸进程的父进程无法正常工作或已终止,可以尝试杀死该父进程。此时,僵尸进程会被操作系统交给 `init` 进程(PID为1)接管,`init` 进程会自动调用 `wait()` 收集僵尸进程的状态信息,从而清除僵尸进程[^3]。 ```bash # 查找僵尸进程及其父进程ID ps -A -ostat,ppid,pid,cmd | grep 'Z' # 杀死父进程 kill -9 <PPID> ``` #### 4. 强制终止僵尸进程 尽管直接杀死僵尸进程本身通常无效,因为僵尸进程已经没有实际运行的代码,但可以通过以下方法间接解决: - **重启相关服务**:如果僵尸进程是由某个特定服务产生的,可以尝试重启该服务以清理相关进程。 - **检查程序逻辑**:分析产生僵尸进程的程序代码,确保父进程正确处理子进程的退出状态。 #### 5. 使用 `top` 和 `ps` 命令监控僵尸进程 通过 `top` 和 `ps` 命令可以实时监控系统中的僵尸进程数量,并采取相应措施。 ```bash # 使用 top 命令查看僵尸进程数量 top -b -n 1 | grep "Z" # 使用 ps 命令查找僵尸进程 ps aux | grep 'Z' ``` --- ### 注意事项 - 僵尸进程本身不会占用 CPU 或内存资源,但会占用进程表中的条目。因此,及时清理僵尸进程对于高并发环境尤为重要[^4]。 - 如果僵尸进程的数量过多,可能会导致系统无法创建新的进程,最终影响系统的正常运行。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值