【Linux取经路】探索进程状态之僵尸进程 | 孤儿进程(二)

简介: 【Linux取经路】探索进程状态之僵尸进程 | 孤儿进程(二)

2.3 D磁盘休眠状态(Disk sleep)

D状态也是一种阻塞状态,在 Linux 系统层面我们称作深度睡眠,S状态称作浅度睡眠。浅度睡眠是可以被唤醒的,即可以响应外部的变化,我们可以通过 kill 指令(其他进程)将浅度睡眠的进程终止掉。下面通过一个情景剧来给大家介绍为什么要有 D 状态,以及 D 状态的作用。

有这样一个场景,一个进程需要向磁盘中写入大量数据。在正常情况下往磁盘中写入数据,进程是需要等待的,等磁盘写完后给进程一个信号,然后进程才能继续去运行。有一天进程A就在向磁盘中写入大量数据,磁盘在写入的过程中,进程A就在内存中翘着二郎腿,嗑着瓜子在等待磁盘写完了给它发信息,此时路过的操作系统发现了进程A,它对进程A说:“我这内存压力都大的不行了,你小子倒好,占着内存不干正事,还在这嗑瓜子!”。于是乎操作系统就将进程A kill 掉了。此时磁盘傻眼了,数据写到一半进程没了,因为进程没了,所以磁盘就把写入的数据删除了,最终结果就是数据没有被写入磁盘。究竟是谁导致了这场悲剧的发生呢?于是乎法官就出来,它先审问操作系统,进程是你 kill 掉的,你怎么解释?操作系统说,我命苦呀,我只是完成了我的本职工作呀,为了给用户提供流畅的运行环境,将一些进程 kill 掉是我的职责呀,这不是我的问题呀。接着法官又来问磁盘,数据是你丢失的,你该如何解释?磁盘说,我祖祖辈辈都是这样工作的呀,进程它让我写入数据,结果自己不见了,其它磁盘遇到这种情况也是将数据丢弃掉呀,你如果判我有罪,那岂不是我的父亲、母亲都有罪呀。最后法官来问进程A,进程还没等法官开口就扑通跪下说,法官大人您明察秋毫呀,我才是被 kill 掉的那个,我属于被害人呀,我怎么会有罪呢。法官听了一圈,感觉大家都没罪,最终法官宣判了,你们三个都没罪,是制度问题,回去我改改操作系统,当进程在向磁盘中写入数据的时候任何人都不能将该进程 kill 掉。于是 D 状态就诞生了。当一个进程处于 D 状态的时候,它不会响应任何请求,任何人和操作系统都不能将该进程 kill 掉。

小Tips:结束掉 D 状态的方法有两种,一是等待某个条件满足,如等待数据写完,二是直接断电。如果被用户查到 D 状态的进程,那就预示着这个操作系统离崩溃不远啦。所以 D 状态会有,但是一般出现的时间都非常短。

2.4 T停止状态(stopped)

在 Linux 内核源代码中我们可以看到连个 T 状态,一个是 T ,一个是 t,我们可以认为这两个 T 状态是一样的,对于一个进程,我们可以通过下面这条指令将它设置成停止状态。

kill -19 进程PID

49f0cfc29f81497b907fdd0a4502abd8.png

可以通过下面这条指令来结束停止状态。

kill -18 进程PID//


212a3b76a1c34374b6356dea89578c08.png

小Tips:结束停止状态的进程会到后台运行,要终止掉这个进程只能通过 kill -9指令。T状态和S状态很像,其中S状态的进程一定是在等待某种资源,而T状态的进程可能是在等待某种资源,也可能是在被其他进程控制。我们在打断点调试一段代码的时候,该进程就会处于T状态。


a795e2def62e43e08a4fff0a33aa7490.png

三、僵尸进程

一个进程在退出时并不是立即将自己所有资源全部释放,当一个进程退出时,操作系统会把当前进程的各种信息维持一段时间,这个状态就叫做 Z 僵尸状态。维持信息是给关心它的“人”,也就是父进程来查看的。如果父进程一直没有来关心退出的子进程,那么这个子进程将长时间处于 Z 状态。

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);                    
            sleep(1);    
            cnt--;    
        }    
       _exit(0);    
    }    
    else 
    {
        while(1)
        {
            printf("我是父进程,PID是:%d,PPID:%d\n",getpid(),getppid());
            sleep(1);
        }
    }
    return 0;
}

上面这段代码在 process 进程中通过调用 fork 接口创建了一个子进程,子进程在执行完五次打印后就会被终止掉,其中的 exit 函数就是用来终止一个进程,父进程将一直运行。

00fee6c0434c486bb742899ca39bc1f2.gif


子进程执行完5次打印后就处于 Z 状态并且后面跟了一个单词 defunct,该单词有死了的,不存在的意思,只不过它还再等父进程来回收它的资源。处于 Z 状态的进程的相关资源尤其是 task_struct 结构体不能被释放。只有当父进程把子进程的相关资源回收后,子进程才能变成 X死亡状态。我们将这种处于 Z 状态的进程就叫做僵尸进程,如果父进程一直不来回收,那这种进程会长时间占用内存资源,造成内存泄漏。

3.1 僵尸进程危害总结

  1. 进程的退出状态必须被维持下去,因为它要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就将一直处于 Z 状态。
  2. 维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在 PCB 对象中,换句话说,Z状态一直不退出,PCB一直都要维护。
  3. 一个父进程如果创建了很多的子进程,就是不回收,会造成内存资源的浪费,因为 PCB 对象本身就要占用内存。
  4. 造成内存泄漏。

四、孤儿进程

上面我们是让子进程先退出,父进程一直运行,接下来我们让父进程先退出,子进程一直运行,看看会有什么结果。

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);    
            sleep(1);    
            cnt--;    
        }    
       _exit(0);    
    }    
    else    
    {
      //父进程    
        int cnt = 5;
        //这里的cnt是5,意味着父进程会先执行结束    
        while(cnt--)    
        {    
            printf("我是父进程,PID是:%d,PPID:%d,cnt:%d\n",getpid(),getppid(),cnt);
            sleep(1);                                                                        
        }    
    }    
    return 0;    
}


ffa7ea09ac3649c7a363bd92b3b508f3.gif

可以看到父进程在执行结束后就只剩下子进程,为什么父进程不会处在 Z僵尸状态呢?答案是父进程也是 bash 的子进程,父进程在执行结束后,它的父进程 bash 会将其回收掉,并且过程非常快,所以我们我们没有看到父进程处在 Z僵尸状态。其次我们发现,当父进程结束后,它的子进程的父进程会变成1号进程,即操作系统。我们将父进程是1号进程的进程叫做孤儿进程,该进程被系统领养。因为孤儿进程未来也会退出,也要被释放,所以它需要被领养。

小Tips:所有的进程只对它的“儿子”,即子进程负责,不会对它的孙子进程负责,因为代码中只有创建子进程的逻辑,并没有创建孙子进程的逻辑,所以并不是不想让爷爷进程来回收孙子进程的资源,是因为爷爷进程没有这个本事,而操作系统会直接从内核层面进行回收,所以当一个进程的父进程结束后,会把该进程交给操作系统,让操作系统来充当它的父进程。

五、结语

今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下,春人的主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是春人前进的动力!

相关实践学习
CentOS 7迁移Anolis OS 7
龙蜥操作系统Anolis OS的体验。Anolis OS 7生态上和依赖管理上保持跟CentOS 7.x兼容,一键式迁移脚本centos2anolis.py。本文为您介绍如何通过AOMS迁移工具实现CentOS 7.x到Anolis OS 7的迁移。
目录
相关文章
|
2月前
|
Linux C++
Linux c/c++进程之僵尸进程和守护进程
这篇文章介绍了Linux系统中僵尸进程和守护进程的概念、产生原因、解决方法以及如何创建守护进程。
30 0
|
5月前
|
Unix Linux
linux进程状态基本语法
linux进程状态基本语法
|
6月前
|
Linux Shell 调度
Linux进程——Linux下常见的进程状态
Linux进程——Linux下常见的进程状态
54 4
|
5月前
|
Java Unix Linux
【Linux】进程状态、进程优先级和进程切换
【Linux】进程状态、进程优先级和进程切换
40 0
|
6月前
|
调度 Python
并发编程 , 孤儿进程 , 守护进程
并发编程 , 孤儿进程 , 守护进程
|
7月前
|
安全 Java
多线程(CAS, ABA问题, Runnable & Callable & 僵尸线程 & 孤儿进程)
多线程(CAS, ABA问题, Runnable & Callable & 僵尸线程 & 孤儿进程)
68 1
|
6月前
|
NoSQL Linux Shell
【linux】进程状态
【linux】进程状态
29 0
|
6月前
|
监控 Linux 应用服务中间件
探索Linux中的`ps`命令:进程监控与分析的利器
探索Linux中的`ps`命令:进程监控与分析的利器
137 13
|
5月前
|
运维 关系型数据库 MySQL
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能
|
5月前
|
弹性计算 Linux 区块链
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
192 4
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
下一篇
DataWorks