Linux 僵尸进程可以被杀死吗?


在 Unix 进程模型中,父进程和其所产生的子进程是异步运行的,所以如果子进程在结束后,会留下一些信息需要父进程使用  wait  /  waitpid  来接收。而如果父进程太忙了,没有调用  wait  /  waitpid  的话,子进程就会变成僵尸进程

僵尸进程不可能被杀死,因为它已经死了,不存在再死一次的问题。死的对立面是活,死者已死。只有活的进程才可能被杀死。

什么是僵尸进程?

首先要明确一点,僵尸进程的含义是:子进程已经死了,但是父进程还没有wait它的一个中间状态,这个时候子进程是一个僵尸。正常情况下子死,父wait,清理掉子进程的task_struct,释放子进程的PID:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>int main(void)
{pid_t pid,wait_pid;int status;pid = fork();if(pid == 1){perror("Cannot create new process\n");exit(1);}else if(pid == 0){printf("child process id:%ld\n",(long)getpid());pause();_exit(0);}else{#if 0printf("ppid :%d\n",getpid());while(1);#endifdo{wait_pid = waitpid(pid,&status,WUNTRACED | WCONTINUED);if(WIFEXITED(status))printf("child process is killed by signal %d\n",WIFSIGNALED(status));}while(!WIFEXITED(status) && !WIFSIGNALED(status));exit(0);}
}

执行

gcc ps_wait.c -pthread && ./a.out

执行情况如下

linux@ubuntu:~/linux$ gcc ps_wait.c -pthread && ./a.out
child process id:4578

每次执行生成的子进程会不同,这里只是举例子说明

运行,我们看到2个a.out进程

杀死子进程4578,看到父进程的打印:

之后,4578会消失,因为父进程执行到了wait,也知道了子进程是被信号2杀掉的。但是如果子进程死了,父进程不执行到wait,比如把上图中的"#if 0"改为"#if 1",杀死子进程后,子进程就会是一个僵尸:

注意,这里说的是子进程会变成一个僵尸进程

代码如下:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>int main(void)
{pid_t pid,wait_pid;int status;pid = fork();if(pid == 1){perror("Cannot create new process\n");exit(1);}else if(pid == 0){printf("child process id:%ld\n",(long)getpid());pause();_exit(0);}else{#if 1printf("ppid :%d\n",getpid());while(1);#endifdo{wait_pid = waitpid(pid,&status,WUNTRACED | WCONTINUED);if(WIFEXITED(status))printf("child process is killed by signal %d\n",WIFSIGNALED(status));}while(!WIFEXITED(status) && !WIFSIGNALED(status));exit(0);}
}

我们重新运行,当我们用kill -2杀掉子进程4628后,我们发现4628成为一个僵尸,状态变为Z+,名字上也加了一个棺材[],成为[a.out]

Z表示的是僵尸进程,[]符号就像一口棺材一样,把这个僵尸进程给装了起来

僵尸不可能被杀死?

我们看到上面4628是个僵尸很不爽,所以我们想把它干掉,据说Linux有个信号9,神挡杀神,佛挡杀佛,我们现在来用kill -9干掉4628

从上图可以看出,我们把4628用kill -9捅了好多刀,但是最后看4628这个僵尸,还是没有消失。

因为僵尸已经是死了,它不可能再次被杀死,你给它捅一万刀,它也是个死人,不可能再次死!

僵尸不可能被杀死,因为它已经死了!

两种方法来清理僵尸进程1、只等父进程来wait清理尸体了2、这个时候我们能够把僵尸消失掉的方法,就是杀死僵尸进程的父进程4627。

一个僵尸可以被杀死的假象

下面的这个程序证明「僵尸可以被杀死」这样的假象。

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>static void *thread_fun(void *param)
{while(1);
}int main(void)
{pthread_t tid;int ret;ret = pthread_create(&tid,NULL,thread_fun,NULL);if(ret == -1){perror("cannot create new thread\n");return -1;}pthread_exit(0);return 0;
}

我们在主线程里面,pthread_create()创建线程后,pthread_exit()退出,这个时候我们会发现,在ps命令里面,a.out显示为一个僵尸进程。这个僵尸进程出现是我们在thread_fun 里面写了一个while(1)不能退出导致的。

继续,开始我们的表演,我们使用下面的命令来杀死这个僵尸进程。

kill -9 4730

我们会惊奇地发现,4730真地会从ps命令里面消失

在没有执行命令杀死 4730 之前,我们「猜测」能杀死僵尸的本质原因是,当主线程4730调用pthread_exit()退出后,主线程4730的状态确实是僵尸了,但是该进程里面的4731线程,却没有死。

我们看看proc 文件系统下面的进程状态

看看4731:

4731是活着的,证明整个进程并没有挂。所以4730的退出,只是让整个进程半死。而由于ps这些命令的误会,4730凑巧又是整个进程的PID,它显示地好像整个4370成了僵尸一样。

4731这个子进程什么时候死了呢?那么,根据POSIX标准关于信号(signal)的定义,当我们执行kill -9 4730 (4730是4730和4731的TGID,也是整个进程用户态视角的PID)的时候,是要杀死整个4730进程的,所以这个时候4731被我们杀死,整个进程就都死了,这个时候,执行到父进程的wait逻辑,导致僵尸消失。

所以,在本例中,kill -9 4730看起来是"杀死了僵尸”,实际是杀死了4730整个进程(里面的每个线程),导致整个进程死。在此之前,整个进程实际还是活的。


扫码或长按关注

回复「篮球的大肚子」进入技术群聊


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/468116.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

jstl 获取 javascript 定义的变量_前端开发大牛完整总结出了JavaScript 难点 +最新web前端开发教程...

1. 立即执行函数立即执行函数&#xff0c;即Immediately Invoked Function Expression (IIFE)&#xff0c;正如它的名字&#xff0c;就是创建函数的同时立即执行。它没有绑定任何事件&#xff0c;也无需等待任何异步操作&#xff1a;立即执行函数function(){…}是一个匿名函数&…

MySQL 5.6.20-enterprise-commercial的参数文件位置问题

今天在折腾MySQL的参数文件时&#xff0c;突然发现MySQL 5.6.20-enterprise-commercial-advanced-log这个版本数据库的参数文件my.cnf的位置有点奇怪&#xff0c;如下所示&#xff1a; [rootDB-Server ~]# mysql --help | grep my.cnforder of preference, my.cnf, $MYSQL_TCP_…

最害怕的是,不知道想要什么

生活在现在的时代&#xff0c;你要是出去吹牛说你与世无争&#xff0c;你看破红尘&#xff0c;你就想开心上班啥也不图&#xff0c;别人要不以为你是一个傻子&#xff0c;要不就以为你是一个家财万贯的主&#xff0c;特别是现在&#xff0c;从2015年开始&#xff0c;房价像吃了…

c语言gcc汇编文件,[汇编]gcc生成汇编.s文件 $ gcc -Og -S sum.c /$ gcc -Og -S -masm=intel sum.c 与 反汇编 objdump...

目录- C语言源码文件&#xff1a;sum.c- gcc 生成的汇编语言文件 sum.sx86-64 AT&T 语法格式x86-64 Intel格式- 反汇编 objdump3.1 输出 AT&T格式 objdump -d sum3.2 输出 Intel格式 objdump -M intel -d sumC语言源码文件&#xff1a;sum.c加法&#xff1a;c a b#in…

diff算法_详解 React 16 的 Diff 策略

这是我 Deep In React 系列的第二篇文章&#xff0c;如果还没有读过的强烈建议你先读前一篇&#xff1a;详谈 React Fiber 架构(1)。前言我相信在看这篇文章的读者一般都已经了解过 React 16 以前的 Diff 算法了&#xff0c;这个算法也算是 React 跨时代或者说最有影响力的一点…

实力剖析一个经典笔试题

昨天群里有个同学发了这样一个题目过来&#xff0c;这个题目应该是嵌入式笔试经典题目了&#xff0c;如果是校招的话&#xff0c;笔试不考这个题目&#xff0c;都觉得有点说不过去。#include <stdio.h>int main() {int a[5] {1,2,3,4,5};int *ptr1 (int *)(&a 1);i…

点击开启此虚拟机时,出现“该虚拟机似乎正在使用中”问题

VMware是常用的虚拟机工具&#xff0c;受到广大群众的喜爱&#xff0c;有用户在意外断电或非正常关机&#xff08;直接关掉进程&#xff09;出现VMware虚拟机无法启动的情况&#xff0c;接下来让我们看看如何解决这一问题。 1.点击我的计算机的windows 10*64后弹出对话框&#…

get请求可以传body吗_都9102年了,GET和POST的区别掌握了没有?

1 前言最近看了一些同学的面经&#xff0c;发现无论什么技术岗位&#xff0c;还是会问到 get 和 post 的区别&#xff0c;而搜索出来的答案并不能让我们装得一手好逼&#xff0c;那就让我们从 HTTP 报文的角度来撸一波&#xff0c;从而搞明白他们的区别。2 标准答案在开撸之前吗…

玩转 Rockchip 的开发板,这些信息你要知道

RK 的处理器Rockchip 在江湖中简称 RK&#xff0c;是一家总部坐落于福建福州的 SOC 设计公司。RK 基于 Arm cortex-A 内核设计的一系列 SOC &#xff0c;宛如苍穹中一颗颗明星&#xff0c;在国内外的产业界和极客手中闪闪发光&#xff0c;其受欢迎程度超过了很多人的想象。登录…

htmlcss实例小项目_小程序websocket心跳库——websocket-heartbeat-miniprogram

前言在16年的时候因为项目接触到websocket&#xff0c;而后对心跳重连做了一次总结&#xff0c;写了篇博客&#xff0c;而后18年对之前github上的demo代码进行了再次开发和开源&#xff0c;最终封装成库。如下&#xff1a;博客&#xff1a;https://www.cnblogs.com/1wen/p/5808…

C语言,画吃豆人剖析

接上一篇文章&#xff0c;很多人还是搞不清楚ptr1[-1]是怎么回事&#xff0c;可以看看这篇文章&#xff0c;看完的同学还是多转发的&#xff0c;让更多的人看到。一道90%都会做错的指针题偶然的一次机会在知乎上看到这个代码&#xff0c;里面涉及的C语言点我觉得很酷&#xff0…

mit数据集_DriveSeg:动态驾驶场景分割数据集

【导读】麻省理工学院和丰田发布DriveSeg数据集以加速自动驾驶研究&#xff0c;DriveSeg包含许多常见道路对象的精确像素级表示&#xff0c;并通过连续视频驾驶场景的镜头。我们如何训练自动驾驶模型&#xff0c;以加深对周围世界的认识&#xff1f;计算机能否从过去的经验中学…

没有长夜痛哭的人,不足以谈人生

早上骑电动车上班了&#xff0c;之前已经连续开车上班有一段时间&#xff0c;每天出公司交停车费心都在滴血&#xff0c;活了30年了&#xff0c;想不到我还是这样穷&#xff0c;我总是觉得明天是最好的一天&#xff0c;即使过得不是十分如意&#xff0c;但是我遇到困难的时候总…

android ne调试工具,Android调试工具adb的正确使用方式

引言&#xff1a;通常在使用Eclipse进行Android应用开发的时候&#xff0c;我们可以使用Eclipse自带的工具来完成调试和查看输出日志的操作&#xff0c;但是假如在当前电脑并没有配置适用于Android开发的Eclipse版本的情况下&#xff0c;想要查看Android设备运行应用或者游戏时…

为什么使用模板

C模板编程是泛型编程的实现方式&#xff0c;因为C中的类型都是强类型。所以导致每个变量都有一种相应的类型。这样就会出现一个弊端&#xff0c;就是相同一个算法针对不同类型的參数&#xff0c;就要实现多个版本号&#xff0c;这样岂不是非常麻烦。那么&#xff0c;C中是否会有…

eltable刷新整个表格方法_利用SSAS实现PBI报表增量刷新

欢迎访问我的博客网站阅读最新BI文章对于经常处理大量数据且其Power BI报表不需要每次都刷新整个数据集的用户而言&#xff0c;增量刷新一直都是个问题&#xff0c;特别是对于那些没有条件使用Premium空间的用户以及在本地报表服务器发布报表的用户。一个稳定可靠的方法是利用S…

我和Git的第一次

我第一次接触Git应该是在2010年&#xff0c;我那年买了第一块Linux开发板&#xff0c;是友善之臂的mini2440&#xff0c;就是这个开发板打开了我Linux的大门&#xff0c;价格是499&#xff0c;我两个月的生活费。如果说到了Linux&#xff0c;不说Git就有点说不过去了&#xff0…

创建线程的方式_创建线程有几种不同的方式?

方式可以用来创建线程1&#xff09;继承Thread类2&#xff09;实现Runnable接口3&#xff09;应用程序可以适用Executor框架来创建线程池实现了Runnable接口这种方式更受欢迎&#xff0c;因为这不需要继承Thread类。在应用设计中已经继承了别的对象的情况下&#xff0c;这需要多…

内存屏障,先看这篇文章

刚看到这个词的时候&#xff0c;我以为是白内障&#xff0c;然后查了很多资料&#xff0c;才理解内存屏障是干嘛的&#xff0c;我就不像很多其他文章说得那么多了&#xff0c;我希望我说得简单一些&#xff0c;让大家看了我的文章都知道这个是怎么回事。说到内存屏障&#xff0…

SpringMVC原理及非注解配置详解

如需转发请标明出处&#xff1a;http://www.cnblogs.com/gudu1/p/7222556.html 1. Spring介绍 Spring MVC是Spring提供的一个强大而灵活的web框架。借助于注解&#xff0c;Spring MVC提供了几乎是POJO的开发模式&#xff0c;使得控制器的开发和测试更加简单。 这些控制器一般不…