【Linux】Linux进程状态和僵尸进程:一篇看懂“进程在忙啥”

前言:欢迎各位光临本博客,这里小编带你直接手撕Make/Makefile (自动化构建),文章并不复杂,愿诸君耐其心性,忘却杂尘,道有所长!!!!

解释_chmod_命令_(3).gif

**🔥个人主页:IF’Maxue-CSDN博客

🎬作者简介:C++研发方向学习者

📖**个人专栏:
《C语言》
《C++深度学习》
《Linux》
《数据结构》
《数学建模》
**

⭐️人生格言:生活是默默的坚持,毅力是永久的享受。不破不立,远方请直行!

文章目录

    • 一、先搞懂:进程状态藏在哪?
    • 二、课本里的核心逻辑:3句话说透
    • 三、基础三态:运行、阻塞、挂起(大白话版)
      • 1. 运行态:“在工位上,要么干活要么等活”
      • 2. 阻塞态:“等材料,暂时离开工位”
      • 3. 挂起态:“工位不够,去临时仓库待着”
    • 四、内核小知识:用“链表”管进程
    • 五、Linux实际进程状态:逐个拆,附代码
      • 1. R状态:运行态(Running)
      • 2. S状态:可中断休眠(浅睡眠)
      • 3. T状态:暂停态(Stopped)
      • 4. t状态:断点态(Trace Stopped)
      • 5. D状态:不可中断休眠(深睡眠)
      • 6. Z状态:僵尸态(Zombie)
      • 7. X状态:死亡态(Dead)
    • 六、孤儿进程:爹跑了,谁管孩子?
      • 为啥要领养?
    • 七、总结:3个核心记牢

一、先搞懂:进程状态藏在哪?

进程不是“黑盒子”,它的所有信息——比如在干啥、代码存在哪、有啥权限——都记在一个叫task_struct的“档案本”里。而进程状态,就是这个档案本里的一个整数,像员工的“工作状态标签”,直接告诉操作系统“这进程现在能干活不”。

image.png

二、课本里的核心逻辑:3句话说透

不管啥操作系统,进程状态的底层逻辑都逃不开这3点(对应你放的课本截图):
image.png

  1. 进程不只有“跑”和“不跑”,有好几种状态;
  2. 状态能互相转——比如“等键盘输入”时会从“能跑”变“等着”,输入完又变回去;
  3. 只有“运行状态”的进程,才真正拿着CPU干活。

三、基础三态:运行、阻塞、挂起(大白话版)

咱们用“工厂干活”的例子理解,比硬记定义简单:

1. 运行态:“在工位上,要么干活要么等活”

操作系统里,每个CPU都有一个“调度队列”(像工厂的“待岗工位”),里面放的全是task_struct(进程档案本)。
只要进程在这个队列里,状态就是运行态(Running) ——不管是正在被CPU“叫去干活”,还是排队等CPU,都算运行态。

调度时,CPU会按规则(比如“先进先出”FIFO)从队列里挑一个档案本,找到对应的代码和数据,让它跑起来。
image.png

2. 阻塞态:“等材料,暂时离开工位”

比如你写了个带scanf的程序——运行后不敲键盘,进程就“卡住了”。这不是它偷懒,是它在等“键盘输入”这个“材料”,这就是阻塞。

操作系统怎么管这种情况?它会给每个硬件(键盘、磁盘、网卡)建个“设备档案”(struct device),档案里专门留了个“等待队列”——所有等这个硬件的进程,都会被移到这个队列里。
简单说:阻塞 = 进程档案本从“调度队列”挪到“硬件等待队列”,直到“材料”备好(比如你敲了键盘),再挪回调度队列。
image.png
image.png
image.png

3. 挂起态:“工位不够,去临时仓库待着”

如果内存实在不够(工厂工位满了),操作系统会把进程的“代码和数据”挪到磁盘(临时仓库),只留task_struct(档案本)在内存——这叫“阻塞挂起”(本来就在等材料,现在连工具都收起来了)。
要是内存还不够,连调度队列里的进程也会被挪去磁盘,叫“运行挂起”(本来在等活,现在先去仓库)。

不过不用记这么细——Linux里不细分挂起态,咱们重点看实际能用的状态就行。
image.png
image.png
image.png

四、内核小知识:用“链表”管进程

操作系统不是瞎管进程的,靠的是“链表”这种数据结构。Linux用的是list_head链表,特点很实用:

  • 每个task_struct里可以放多个list_head(像一个员工有多个“身份标签”);
  • 这样一个进程能同时属于多个链表——比如既在“进程组链表”,又在“调度队列链表”;
  • 想通过list_head找到完整的task_struct?靠“地址偏移”——知道list_head在档案本里的位置,就能算出整个档案本的地址。

简单说:进程状态切换,本质就是list_head在不同链表间“挪位置”,无非是增删查改的操作。
image.png
image.png
image.png
image.png
image.png
image.png

五、Linux实际进程状态:逐个拆,附代码

Linux的进程状态存在task_state_array里,咱们逐个讲,每个都给代码例子,跟着做就能看懂。

1. R状态:运行态(Running)

  • 特点:在调度队列里,要么正在跑,要么等CPU;不能被kill打断(想停它得等它自己出队列)。
  • 为啥有时查不到?比如printf这种操作太快,进程瞬间切到其他状态,得用“死循环不做IO”才能稳定抓到。

代码例子(抓R状态):

#include <stdio.h>
int main() {while(1); // 死循环,不做任何IO,一直待在调度队列return 0;
}
  • 操作步骤:
    1. 编译:gcc test.c -o test
    2. 后台运行(不占终端):./test &
    3. 查看状态:ps aux | grep test,会看到状态是R

image.png
image.png
image.png
image.png
(注:状态后的+表示“前台进程”,后台进程没有+

2. S状态:可中断休眠(浅睡眠)

  • 特点:等资源(键盘、文件),处于“浅睡”;能被kill命令打断(比如等输入时,kill一下就退出)。
  • 最常见的阻塞态,比如scanf等输入、读文件时都算S状态。

代码例子(抓S状态):

#include <stdio.h>
int main() {int a;scanf("%d", &a); // 等键盘输入,进程阻塞,状态变Sprintf("%d\n", a);return 0;
}
  • 操作步骤:
    1. 运行:./test,不输入任何内容;
    2. 另开终端查状态:ps aux | grep test,状态是S
    3. 测试kill:kill 进程号,进程会直接退出(S状态能被打断)。

image.png
image.png

3. T状态:暂停态(Stopped)

  • 特点:进程被“暂停”,既不在调度队列也不在等待队列;得用命令恢复kill -18)。
  • 触发方式:按ctrl+z暂停前台进程,或用kill -19手动暂停。

操作例子

  1. 运行上面的scanf程序:./test
  2. ctrl+z,终端提示“已暂停”;
  3. 查状态:ps aux | grep test,状态是T
  4. 恢复:kill -18 进程号,进程继续等输入;
  5. 再暂停:kill -19 进程号,变回T。

image.png

4. t状态:断点态(Trace Stopped)

  • 特点:只有调试时会出现!进程在断点处停下,比如gdb设断点后运行。

操作例子

  1. 用gdb调试:gdb ./test
  2. 设断点:b main(在main函数开头停);
  3. 运行:r
  4. 另开终端查状态:ps aux | grep test,状态是t

image.png
image.png

5. D状态:不可中断休眠(深睡眠)

  • 特点:等“关键资源”(比如磁盘IO),处于“深睡”;kill -9都杀不掉(怕打断磁盘操作导致数据损坏)。
  • 常见场景:用dd命令拷贝大文件时。

操作例子(抓D状态):

  1. 执行磁盘读写命令:dd if=/dev/zero of=/tmp/test bs=1G count=10(往/tmp写10G文件);
  2. 另开终端查状态:ps aux | grep dd,状态是D
  3. 试杀:kill -9 进程号,进程纹丝不动,直到磁盘操作完成才退出。

image.png
image.png

6. Z状态:僵尸态(Zombie)

  • 特点:子进程退出了,但父进程没“要它的退出信息”(比如退出码),只剩task_struct(档案本)在内存;不能被调度,也杀不掉(因为进程已经死了,只剩空壳)。
  • 风险:僵尸进程占内存,父进程一直不管就会“内存泄露”(尤其是开机就跑的常驻进程)。

代码例子(造僵尸进程):

#include <stdio.h>
#include <unistd.h>
int main() {pid_t pid = fork(); // 创建子进程if (pid == 0) {// 子进程:直接退出,没被回收printf("子进程PID:%d\n", getpid());return 0;} else if (pid > 0) {// 父进程:死循环,不回收子进程while(1) sleep(1); }return 0;
}
  • 操作步骤:
    1. 运行:./test
    2. 查状态:ps aux | grep test,会看到子进程状态是Z
    3. 解决办法:让父进程调用wait()回收,或杀掉父进程(子进程会被领养)。

image.png
(注:slab是Linux内核的内存分配机制,专门管理像task_struct这样的小对象,僵尸进程的task_struct就存在这里,不回收会占 slab 内存)
image.png

7. X状态:死亡态(Dead)

  • 特点:进程彻底退出,所有资源(代码、数据、task_struct)被OS回收;看不到这个状态(因为瞬间就没了),只是理论上的状态。

六、孤儿进程:爹跑了,谁管孩子?

如果父进程先退出,子进程没人管,就成了“孤儿进程”——这时候Linux会让1号进程领养它:

  • 新内核(比如Ubuntu、CentOS 7+):1号进程是systemd
  • 老内核:1号进程是init

为啥要领养?

怕孤儿进程退出后没人回收,变成僵尸进程,导致内存泄露。1号进程会负责“要它的退出信息”,相当于“福利院”。

代码例子(造孤儿进程):

#include <stdio.h>
#include <unistd.h>
int main() {pid_t pid = fork();if (pid == 0) {// 子进程:睡10秒,等父进程先退出printf("子进程PID:%d,当前父进程PID:%d\n", getpid(), getppid());sleep(10); printf("子进程现在父进程PID:%d\n", getppid()); // 会变成1return 0;} else if (pid > 0) {// 父进程:马上退出,不管子进程printf("父进程退出,PID:%d\n", getpid());return 0;}return 0;
}
  • 操作步骤:
    1. 运行:./test
    2. 父进程瞬间退出,子进程一开始的父进程是终端(比如bash);
    3. 10秒后,子进程的父进程变成1(被1号进程领养);
    4. 孤儿进程会变成后台进程,终端看不到它的输出(除非重定向)。

image.png
image.png
image.png
image.png
image.png
image.png

七、总结:3个核心记牢

  1. 进程状态本质:task_struct在不同链表间挪位置(调度队列、等待队列);
  2. Linux重点状态:R(跑)、S(浅睡等资源)、D(深睡杀不掉)、Z(僵尸要回收)、T(暂停);
  3. 孤儿/僵尸处理:孤儿找1号进程领养,僵尸靠父进程wait()回收,避免内存泄露。

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

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

相关文章

开源视频剪辑工具推荐

开源视频剪辑和处理工具近年来发展非常迅速&#xff0c;许多工具的功能已经足以媲美甚至超越一些商业软件。以下是一些顶级的开源视频编辑和处理工具&#xff0c;涵盖了从简单易用到专业级别的不同需求。 一、 主要视频编辑软件&#xff08;非线性编辑&#xff0c;NLE&#xf…

第十四届蓝桥杯青少组C++国赛[2023.5.28]第二部分编程题(4、 数独填数)

参考程序&#xff1a;#include <bits/stdc.h> using namespace std;char board[9][9];// 检查在 (r,c) 填 num 是否有效 bool isValid(int r, int c, char num) {for (int i 0; i < 9; i) {if (board[r][i] num) return false; // 同行if (board[i][c] num) r…

C语言中奇技淫巧08-使用alloca/__builtin_alloca从栈上分配空间

alloca是什么? alloca 是一个非标准但广泛支持的 C 语言函数&#xff0c;用于在当前函数的栈&#xff08;stack&#xff09;上动态分配内存。 与 malloc 的区别&#xff1a; malloc 在堆&#xff08;heap&#xff09; 上分配内存&#xff0c;需要手动调用 free 释放。alloca 在…

【Markdown转Word完整教程】从原理到实现

Markdown转Word完整教程&#xff1a;从原理到实现 前言 在技术文档编写和学术论文创作中&#xff0c;Markdown因其简洁的语法和良好的可读性而广受欢迎。然而&#xff0c;在实际工作中&#xff0c;我们经常需要将Markdown文档转换为Word格式&#xff0c;以便与同事协作、提交正…

IBM穿孔卡片:现代计算技术的奠基之作

本文由「大千AI助手」原创发布&#xff0c;专注用真话讲AI&#xff0c;回归技术本质。拒绝神话或妖魔化。搜索「大千AI助手」关注我&#xff0c;一起撕掉过度包装&#xff0c;学习真实的AI技术&#xff01; 1 打孔卡概述 穿孔卡片&#xff08;Punch Card&#xff09;又称打孔卡…

亚马逊旺季来临如何用woot冲刺

在亚马逊旺季来临之际&#xff0c;使用Woot冲刺需结合新品推广、老品激活、库存清理等不同场景&#xff0c;通过精准选品、活动设置、广告配合及数据监控实现销量与排名的双重提升。以下是具体操作指南&#xff1a;一、精准选品&#xff1a;匹配提报条件新品期选品标准&#xf…

AlexNet:计算机视觉的革命性之作

AlexNet: Revolutionizing Deep Learning for Computer Vision (1)网络提出的背景 论文题目:ImageNet Classification with Deep Convolutional Neural Networks arXiv地址:https://arxiv.org/abs/1207.0575 在2012年ImageNet大规模视觉识别挑战赛(ILSVRC)中,AlexNet以15…

【高等数学】第十一章 曲线积分与曲面积分——第二节 对坐标的曲线积分

上一节&#xff1a;【高等数学】第十一章 曲线积分与曲面积分——第一节 对弧长的曲线积分 总目录&#xff1a;【高等数学】 目录 文章目录1. 对坐标的曲线积分的概念与性质1. 对坐标的曲线积分的概念与性质 变力沿曲线所作的功 先用曲线 LLL 上的点 M1(x1,y1),M2(x2,y2),…,M…

解析SQL Server核心服务与功能

SQL Server 安装后会在 Windows 系统中注册多个服务&#xff0c;每种服务负责不同的功能。主要服务类型包括&#xff1a; &#x1f4cc; 核心服务 (必须或常用)SQL Server Database Engine (数据库引擎服务) 服务名称格式&#xff1a; MSSQL$<InstanceName> (命名实例) 或…

专项智能练习(计算机动画基础)

1.小明在制作Flash作品时&#xff0c;舞台及库中素材如第下图所示&#xff0c;把“马”元件插入到“马”图层第1帧并放在舞台的草地位置&#xff0c;发现舞台中并无马图像显示&#xff0c;下列情形中最有可能的是&#xff08; &#xff09;。A.“马”图层已被锁定 B.“马”图层…

第三方库集成:结合 Express.js 构建本地服务器

引言&#xff1a;Express.js 在 Electron 第三方库集成中的本地服务器构建价值 在 Electron 框架的第三方库集成生态中&#xff0c;Express.js 作为 Node.js 的经典 Web 框架&#xff0c;扮演着构建本地服务器的关键角色。它不仅仅是一个路由和中间件工具&#xff0c;更是 Elec…

百度地图+vue+flask+爬虫 推荐算法旅游大数据可视化系统Echarts mysql数据库 带沙箱支付+图像识别技术

F012 百度地图vueflask爬虫 推荐算法旅游大数据可视化系统Echarts mysql数据库 带沙箱支付图像识别技术 &#x1f4da;编号&#xff1a; F012 文章结尾部分有CSDN官方提供的学长 联系方式名片 博主开发经验15年,全栈工程师&#xff0c;专业搞定大模型、知识图谱、算法和可视化…

# 开发中使用——鸿蒙CoreSpeechKit让文字发声后续

开发中使用——鸿蒙CoreSpeechKit让文字发声后续 设置音量大小 volume// 设置播报相关参数this.extraParam {"queueMode": 0, "speed": AppModel.speed, "volume": AppModel.volume, "pitch": 1, "languageContext": zh-CN,…

Java全栈开发面试实录:从基础到微服务的深度探索

Java全栈开发面试实录&#xff1a;从基础到微服务的深度探索 面试官与应聘者的初次见面 面试官&#xff1a;你好&#xff0c;很高兴见到你。请先做个自我介绍吧。 应聘者&#xff1a;您好&#xff0c;我叫李明&#xff0c;今年28岁&#xff0c;是南京大学计算机科学与技术专业的…

前端路由切换不再白屏:React/Vue 实战优化全攻略(含可运行 Demo)

摘要 在单页应用&#xff08;SPA&#xff09;开发中&#xff0c;React、Vue、Angular 这些主流框架都依赖前端路由来完成页面切换。好处是显而易见的&#xff1a;首屏资源一次加载&#xff0c;后续页面切换靠前端路由完成&#xff0c;体验比传统的多页应用要顺畅很多。 但是在实…

C#之LINQ

文章目录前言LINQ一、LINQ1一、LINQ2一、LINQ3Where方法&#xff1a;每一项数据都会进过predicate的测试&#xff0c;如果针对一个元素&#xff0c;predicate执行的返回值为true&#xff0c;那么这个元素就会放到返回值中。获取一条数据&#xff08;是否带参数的两种写法&#…

第 2 讲:Kafka Topic 与 Partition 基础

课程概述 在第一篇课程中&#xff0c;我们了解了 Kafka 的基本概念和简单的 Producer/Consumer 实现。 本篇课程将深入探讨 Kafka 的核心机制&#xff1a;Topic 和 Partition。 学习目标 通过本课程&#xff0c;您将掌握&#xff1a; Topic 和 Partition 的设计原理&#x…

三阶Bezier曲线曲率极值及对应的u的计算方法

三阶&#xff08;三次&#xff09;Bezier曲线的曲率极值及其对应的参数 u 的计算是一个复杂的非线性优化问题。由于三阶Bezier曲线是参数化曲线&#xff0c;其曲率表达式较为复杂&#xff0c;通常无法通过解析方法直接求得所有极值点&#xff0c;但可以通过求解曲率导数为零的方…

Unity:XML笔记(二)——Xml序列化、反序列化、IXmlSerializable接口

写在前面&#xff1a;写本系列(自用)的目的是回顾已经学过的知识、记录新学习的知识或是记录心得理解&#xff0c;方便自己以后快速复习&#xff0c;减少遗忘。三、Xml序列化序列化就是把想要存储的内容转换为字节序列用于存储或传递。1、序列化我们先创建一个类&#xff0c;之…

java注解、Lambda表达式、Servlet

一、Java注解注解的概念&#xff1a; Java注解是代码中的元数据&#xff0c;可以用于描述其他代码。注解在编译、类加载、运行时被处理&#xff0c;并且不会改变代码逻辑。注解的用途&#xff1a; 提供代码元信息&#xff0c;如 Override 表明一个方法覆盖了父类的方法。 编译检…