【Linux】进程概念(三):从 R/S/D/T 到僵尸 / 孤儿进程

【Linux】进程概念(三):从 R/S/D/T 到僵尸 / 孤儿进程

摘要

Linux进程状态是进程在其生命周期中所处的不同状况,主要包括运行(R)、睡眠(S)、磁盘休眠(D)、停止(T)、僵尸(Z)和死亡(X)。这些状态反映了进程是正在执行、等待资源还是已经终止。其中,僵尸状态是一个关键概念,指子进程已退出但其退出信息未被父进程回收,导致其进程控制块无法释放,从而占用系统资源。若父进程先退出,子进程则会成为“孤儿进程”,并由1号init进程接管,以确保其最终能被正确回收,避免资源泄漏。

在这里插入图片描述


目录

一、Linux进程状态

一个进程从创建而产生至撤销而消亡的整个生命期间,有时占有处理器执行,有时虽然可以运行但分不到处理器,有时虽然有空闲处理器但因等待某个时间的发生而无法执行,这一切都说明进程和程序不相同,进程是活动的且有状态变化的,于是就有了进程状态这一概念。

在这里插入图片描述
//linux源代码如下staticconstchar*const task_state_array[]={"R (running)"//运行 /* 0*/"S (sleeping)"//睡眠 /* 1*/"D (disk sleep)"//深度睡眠 /* 2*/"T (stopped)"//停止 /* 4*/"t (tracing stop)"//追踪停止 /* 8*/"X (dead)"//死亡 /* 16*/"Z (zombie)",//僵尸 /* 32*/};

注意:

  1. 进程的当前状态是保存到自己的进程控制块(PCB)当中的,在Linux操作系统当中也就是保存在task_struct当中的。
  2. 在Linux操作系统当中我们可以通过 ps aux 或 ps axj 命令查看进程的状态。
ps ajx

ps aux

1. R(runing)— 运行状态

运行状态:一个进程处于运行状态,并不说明他一定正在运行中。它可能正在CPU中运行;也可能在CPU对应的运行队列中准备接受调度。
//通过一个死循环来查看运行状态1 #include<stdio.h>2intmain()3{4while(1)5{67}89return0;10}
在这里插入图片描述
此时我们可以看到STAT(state)状态栏那一栏我们的进程对应的状态为R+,R是运行状态,+代表是前台运行,没有+就是后台运行。我们的死循环程序会不断的重复进行代码的执行,占用CPU的资源,它也不满足访问外事进入到阻塞状态或者其他状态的情况,所以它是运行状态。

2. S(sleeping)— 睡眠状态

睡眠状态说明进程在等待某件事情的完成。处于浅度睡眠状态的进程随时可以被唤醒,也可以被杀掉(这里的睡眠有时候也可叫做可中断睡眠(interruptible sleep))。
#include<stdio.h>#include<unistd.h>intmain(){while(1){printf("i am a process\n");sleep(1);// 睡眠1秒}return0;}

我们通过ps ajx | head -1 && ps ajx | grep 文件名来查看它的状态

在这里插入图片描述
  • 虽然从代码逻辑上看,程序确实在不断地循环执行,但在操作系统层面的进程调度视角下,情况却有所不同。
  • 当程序执行到 sleep(1) 时,会发生一个关键的状态转变:进程会主动让出 CPU 的使用权,并进入等待队列。此时操作系统内核会将这个进程标记为睡眠状态(S 状态),因为进程明确表示它将在接下来的1秒钟内不需要 CPU 资源。这种睡眠不是被动的等待,而是进程通过系统调用主动发起的请求。
  • 在这个过程中,printf 函数的执行确实需要极短的 CPU 时间,但相对于整整1秒的睡眠期来说,这个执行时间几乎可以忽略不计。当 printf 完成输出后,进程立即进入睡眠状态,在接下来的1秒内都处于这种等待状态。操作系统的进程调度器在扫描进程状态时,几乎总是捕捉到进程在睡眠而不是在运行。
  • 更重要的是,sleep() 函数在底层是通过设置定时器并调用等待函数来实现的。进程会挂起自己,直到定时器超时才会被重新唤醒。
#include<stdio.h>intmain(){int a =0;scanf("%d",&a);return0;}

我们的程序需要从键盘上读取数据,不断等待外设键盘准备好,如果我们不去敲击键盘输入数据,那么键盘就会一直不准备好,那么此时进程就会在键盘设备的阻塞队列中等待,此时就对应操作系统学科的阻塞状态,所以此时我们的进程处于休眠状态

在这里插入图片描述
而处于浅度睡眠状态的进程是可以被杀掉的,我们可以使用kill命令将该进程杀掉。
在这里插入图片描述

3. D(disk sleep)— 磁盘休眠状态

D磁盘休眠状态(disk sleep): 也叫做深度睡眠,处于这个状态的进程通常会等待IO的结束,不响应任何请求,同时其也对应操作系统学科上的阻塞状态的一种特殊情况,处于深度睡眠的进程,不响应操作系统的任何请求,也无法被 kill -9杀死
您描述的场景很好地解释了深度睡眠(Uninterruptible Sleep)的设计原理和必要性。在这个假设的磁盘写入困境中,确实展现了一个典型的数据完整性保护机制。

当进程向磁盘发起写入请求后,便进入等待回应的状态。

  • 此时如果操作系统因内存资源严重不足而决定终止该进程,就会引发数据一致性问题。进程被强制杀死后,其内存中的代码和数据随之释放,而磁盘那边由于空间不足也丢弃了待写入的数据,这就造成了不可挽回的数据丢失。在这种复杂情境下,操作系统为了维护系统稳定性而清理资源,磁盘为了最大化利用有限空间而优先处理可完成的任务,各自的行为从局部视角看都有其合理性,但整体协作却导致了用户数据的损失。
  • 为了解决这种责任边界模糊但后果严重的数据丢失风险,系统引入了深度睡眠状态。处于深度睡眠的进程对操作系统的终止信号不予响应,就像进入了一种受保护的休眠。只有当磁盘完成操作并返回明确结果后,进程才会苏醒。这种机制确保了数据操作的原子性——要么完整写入,要么完全失败,但绝不会停留在悬而未决的中间状态。

4. T(stopped)— 停止状态

在Linux当中,我们可以通过发送SIGSTOP信号使进程进入暂停状态(stopped),发送SIGCONT信号可以让处于暂停状态的进程继续运行。

#include<stdio.h>#include<sys/types.h>#include<unistd.h>intmain(){while(1){printf("i am proc,my PID:%d,my PPID:%d\n",getpid(),getppid());sleep(6);}return0;}

暂停:kill -SIGSTOP PID或者kill -19 PID

在这里插入图片描述


继续:kill -SIGCONT PID或者kill -18 PID

在这里插入图片描述

6. X(dead)— 死亡状态

注意:这个状态只是一个返回状态,并不会在任务列表中看到这个状态,当Z僵尸状态结束后就会进入Z死亡状态

二、僵尸状态进程

1. 定义

僵尸进程(zombies):子进程退出的时候,如果父进程没有主动读取回收子进程的信息,那么子进程会让自己一直处于Z僵尸状态,即对应子进程相关资源尤其是task_struct结构体不能释放。

exit系统调用接口可以终止一个进程,使用exit可以保证我们的子进程或父进程被终止

在这里插入图片描述
#include<stdio.h>#include<stdlib.h>#include<unistd.h>intmain(){printf("I am running...\n"); pid_t id =fork();if(id ==0){//childint count =5;while(count){printf("I am child...PID:%d, PPID:%d, count:%d\n",getpid(),getppid(), count);sleep(1); count--;}printf("child quit...\n");exit(1);}elseif(id >0){//fatherwhile(1){printf("I am father...PID:%d, PPID:%d\n",getpid(),getppid());sleep(1);}}else{//fork error}return0;}
在这里插入图片描述


程序开始运行时,初始进程处于睡眠状态,这是因为它等待外设屏幕输出主动让出CPU。当fork系统调用执行后,创建出的子进程与父进程各自进入独立的执行流,两者都因包含sleep调用而周期性处于睡眠状态。

子进程在完成五次输出后通过exit正常退出,此时进程的执行已经结束,但由于父进程正忙于自己的循环输出而没有调用wait系列函数来回收子进程的退出状态,子进程便进入了僵尸状态。僵尸状态的特点是进程的执行已终止,核心资源已被释放,但在进程表中仍保留着退出状态信息等待父进程读取,这时使用ps命令便能观察到标记为Z的子进程。


2. 危害

  • 僵尸进程的长期存在确实会带来显著的系统资源浪费问题。当一个子进程结束后,其退出状态信息必须被保留在进程控制块(PCB)中,这是为了向父进程汇报任务执行的结果。这些状态数据并非凭空存在,它们需要占用实实在在的内存空间来存储。
  • 如果父进程始终不去读取子进程的退出状态,那么子进程就会一直保持僵尸状态。这意味着操作系统必须持续维护这些已经终止进程的PCB结构,而每个PCB都包含着进程的详细信息,如同C语言中每个结构体变量都需要在内存中分配特定空间一样。
    • 设想一个父进程频繁创建大量子进程却从不进行回收的情况,这会导致系统中积累越来越多的僵尸进程。每个僵尸进程的PCB都无法被释放,它们占据的内存空间也就无法被重新利用。这种场景本质上就是一种内存泄漏——系统的宝贵内存资源被这些已经死亡但未被清理的进程残留信息所占据,可用的内存空间逐渐减少,最终可能影响系统的整体性能和稳定性。因此,及时回收子进程不仅是良好的编程习惯,更是保证系统健康运行的重要措施。

三、孤儿进程

在Linux当中的进程关系大多数是父子关系,若子进程先退出而父进程没有对子进程的退出信息进行读取,那么我们称该进程为僵尸进程。但若是父进程先退出,那么将来子进程进入僵尸状态时就没有父进程对其进行处理,此时该子进程就称之为孤儿进程。

若是一直不处理孤儿进程的退出信息,那么孤儿进程就会一直占用资源,此时就会造成内存泄漏。因此,当出现孤儿进程的时候,孤儿进程会被1号init进程领养,此后当孤儿进程进入僵尸状态时就由int进程进行处理回收。

#include<stdio.h>#include<stdlib.h>#include<unistd.h>intmain(){printf("I am running...\n"); pid_t id =fork();if(id ==0){//childint count =5;while(1){printf("I am child...PID:%d, PPID:%d\n",getpid(),getppid(), count);sleep(1);}}elseif(id >0){//fatherint count =5;while(count){printf("I am father...PID:%d, PPID:%d, count:%d\n",getpid(),getppid(), count);sleep(1); count--;}printf("father quit...\n");exit(0);}else{//fork error}return0;}
在这里插入图片描述


在这里插入图片描述
  1. 当执行程序后,打印字符串,然后进程调用了fork会创建子进程,当前进程成为父进程,子进程和父进程通过if分流,进入不同的执行流去执行不同的代码块,使用ps查看此时子进程和父进程都是处于S+睡眠状态(前台进程)
  2. 子进程会一直死循环间隔睡眠1s打印进程的PID和PPID
  3. 父进程会打印5次进程的PID和PPID后被exit系统调用终止进程,那么此时子进程的父进程的进程信息被bash回收释放了,所以子进程就没有父进程回收进程信息了。
  4. 操作系统肯定不能让这种事情发生,因为任何一个进程都要退出,也要被释放,那么此时操作系统收养了当前的子进程。
  5. 注意此时的子进程被操作系统收养,在后台运行,并且写的是死循环打印,那么子进程不会主动退出,那么只能使用kill -9 进程标识符,杀死子进程进行退出。
这里的bash进程算的上是当前子进程的爷爷进程,可是任何一个父进程只对子进程负责,即bash使用fork创建出来的当前子进程的父进程, bash只对当前子进程的父进程负责,bash的代码逻辑中没有要为当前子进程负责的逻辑,所以bash不能回收当前的子进程。

总结

总的来说,Linux进程状态机制是操作系统进行资源管理和调度的核心。运行、睡眠、停止等状态确保了CPU等资源的合理分配;而僵尸和孤儿进程则揭示了进程间父子关系与资源回收的重要性。深刻理解这些状态及其转换,对于诊断进程问题、编写健壮的程序(尤其是正确回收子进程资源)以及维护系统稳定性和性能至关重要,是系统编程和运维的基础知识。


✨ 坚持用清晰易懂的图解+代码语言, 让每个知识点都简单直观!
🚀 个人主页不呆头 · ZEEKLOG
🌱 代码仓库不呆头 · Gitee
📌 专栏系列 :📖 《C语言》🧩 《数据结构》💡 《C++》🐧 《Linux》💬 座右铭 :“不患无位,患所以立。”

Read more

“裸奔龙虾”数量已达27万只,业内人士警告;AI浪潮下,中传“砍掉”翻译等16个专业;薪资谈判破裂,三星电子8.9万人要罢工 | 极客头条

“裸奔龙虾”数量已达27万只,业内人士警告;AI浪潮下,中传“砍掉”翻译等16个专业;薪资谈判破裂,三星电子8.9万人要罢工 | 极客头条

「极客头条」—— 技术人员的新闻圈! ZEEKLOG 的读者朋友们好,「极客头条」来啦,快来看今天都有哪些值得我们技术人关注的重要新闻吧。(投稿或寻求报道:[email protected]) 整理 | 郑丽媛 出品 | ZEEKLOG(ID:ZEEKLOGnews) 一分钟速览新闻点! * “裸奔龙虾”已高达27万只!业内人士警告:一旦黑客入侵,敏感信息一秒搬空 * 阿里云 CTO 周靖人代管千问模型一号位,刘大一恒管理更多团队 * 中国传媒大学砍掉翻译、摄影等 16 个本科专业,直言教育要面向人机分工时代 * 雷军放话:小米将很快推出 L3、L4 的驾驶 * 消息称原理想汽车智驾一号位郎咸朋具身智能赛道创业 * vivo 前产品经理宋紫薇创业,瞄准 AI 时尚Agent,获亿元融资 * MiniMax 发布龙虾新技能,股价暴涨超 23% * 薪资谈判破裂,三星电子

By Ne0inhk
Python热度下滑、AI能取代搜索引擎?TIOBE最新榜单揭晓!

Python热度下滑、AI能取代搜索引擎?TIOBE最新榜单揭晓!

整理 | 屠敏 出品 | ZEEKLOG(ID:ZEEKLOGnews) 日前,TIOBE 发布了最新的 3 月编程语言榜单。整体来看,本月排名变化不算大,但榜单中仍然出现了一些值得关注的小波动。  AI 工具能帮大家秒懂最新编程语言趋势? 由于 2 月天数较少,3 月的榜单整体变化有限。借着这次发布,TIOBE CEO Paul Jansen 也回应了一个最近被频繁讨论的问题:为什么 TIOBE 指数仍然依赖搜索引擎统计结果?在大语言模型流行的今天,直接询问 AI 哪些编程语言最流行,是不是更简单? 对此,Jansen 的回答是否定的。 他解释称,TIOBE 指数本质上统计的是互联网上关于某种编程语言的网页数量。而大语言模型的训练数据同样来自这些网页内容,因此从信息来源来看,两者并没有本质区别。换句话说,LLM 的判断,本质上也是建立在这些网页数据之上的。 Python 活跃度仍在下降

By Ne0inhk
一天开13个会、一个Bug要修200天!前亚马逊L7爆料:这轮大裁员,AI只是“背锅侠”

一天开13个会、一个Bug要修200天!前亚马逊L7爆料:这轮大裁员,AI只是“背锅侠”

整理 | 郑丽媛 出品 | ZEEKLOG(ID:ZEEKLOGnews) 过去一年,大型科技公司的裁员消息几乎从未停过。但当公司对外给出的理由越来越统一,“AI 让组织更高效”,也有越来越多内部员工开始提出另一种质疑:事情或许没那么简单。 最近,一段来自前亚马逊员工 Becky 的 YouTube 视频在开发者社区流传开来。她曾在亚马逊工作 7 年,其中 5 年担任 L7 级别的技术管理者,负责过团队年度规划(OP1)等核心管理工作——可去年,她主动离开了亚马逊。 就在最近,她的三位前同事接连被裁,其中两人还是 H-1B 签证员工,都背着房贷压力。其中一位同事忍不住给 Becky 发消息:“你去年离开的时候,是不是已经预料到会发生这些?” 对此,Becky 的回答很坦诚:她不知道具体什么时候会裁员,但她早就感觉情况不对劲了。 在她看来,这轮裁员被归因为

By Ne0inhk
用 10% GPU 跑通万亿参数 RL!马骁腾拆解万亿参数大模型的后训练实战

用 10% GPU 跑通万亿参数 RL!马骁腾拆解万亿参数大模型的后训练实战

整理 | 梦依丹 出品 | ZEEKLOG(ID:ZEEKLOGnews) 左手是提示词的工程化约束,右手是 Context Learning 的自我进化。 在 OpenAI 新发布的《Prompt guidance for GPT-5.4》中,反复提到了 Prompt Contracts(提示词合约)。要求开发者像编写代码一样,严谨地定义 Agent 的输入边界、输出格式与工具调用逻辑,进而换取 AI 行为的确定性。 但在现实操作中,谁又能日复一日地去维护那些冗长、脆弱的“提示词代码”? 真正的 Agent,不应只靠阅读 Context Engineering,更应该具备 Context Learning 的能力。 为此,在 4 月 17-18

By Ne0inhk