首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【Linux系统】掌握进程状态:运行/阻塞/暂停/僵尸/孤儿......

【Linux系统】掌握进程状态:运行/阻塞/暂停/僵尸/孤儿......

作者头像
落羽的落羽
发布2025-12-18 19:43:03
发布2025-12-18 19:43:03
2120
举报

在这里插入图片描述
在这里插入图片描述

各位读者大佬好,我是落羽!一个坚持不断学习进步的学生。 如果您觉得我的文章还不错,欢迎多多互三分享交流,一起学习进步! 也欢迎关注我的blog主页: 落羽的落羽

进程状态

进程,是有不同的状态的。

在Linux内核源码中能看到这样的代码:

在这里插入图片描述
在这里插入图片描述

实际上这些就是进程不同的状态。

查询进程状态,使用ps命令,有一列stat,即代表进程的状态

在这里插入图片描述
在这里插入图片描述

1. 运行状态(running)与睡眠状态(sleeping)

  • R:运行状态,表示进程是在运行中的,或是在运行队列中!
  • S:睡眠状态,其实代表着阻塞状态或浅度睡眠状态(也叫可中断睡眠状态interruptible sleep),代表着进程在等待着某种事件完成(如执行sleep函数、scanf等待读取数据等)

进程的运行队列,是操作系统内核中管理进程的数据结构,通常是一个链表的队列,存放着的就是进程的task_struct,用于记录所有等待CPU调度的进程,方便快速选择下一个执行的进程。进程处于R状态时,必然在 CPU 的调度队列(运行队列)中! 每个计算机硬件都有一个这样的队列。比如键盘,当一个进程运行到了scanf语句,需要等用户从键盘输入数据,此时这个进程的task_struct就会移动到键盘的调度队列中等待读取,读取到数据后再移动到CPU的调度队列中。其他计算机硬件也是类似的道理。

当代大多数计算机,会给每一个进程分配一个时间片。它的时间片执行完,就会自动让出CPU,另一个进程接着执行。CPU内只有一套寄存器,保存着进程的临时数据,它的时间结束后,这些临时数据转移到task_struct中的成员变量“上下文数据”中存放,寄存器中继续保存下一个进程的临时数据。再轮到这个进程执行时,临时数据再转移到寄存器中。

举个例子:

下面这段代码,执行后,会不断循环打印一句hello后睡眠一秒。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

查询发现这个进程是S状态,但其实只有程序执行到sleep语句时才会是S状态,如果你恰好能在执行printf语句的那几纳秒时执行了ps查询,就会显示是R状态。可惜几纳秒相比sleep的一秒占比太小了,我反复查询了好多次也没遇到显示R,大家也可以再去试试。

这段代码很简单,等待读取一个数:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

当我还没有输入数据时,进程等待中,进程的task_struct在键盘设备的等调度队列中,也是S状态。

换句话说,运行状态与阻塞状态的本质是:看进程的task_struct是在CPU还是其他硬件的调度队列中!

2. 深度睡眠状态(disk sleep)

D:深度睡眠状态、不可中断休眠状态,是进程进行磁盘等外设IO操作时的状态,是Linux特有的一种进程状态。

刚才说的S状态,除了可以输入ctrl c中断进程,其实还可以使用kill命令: kill主要用于向进程发送信号,它有非常多的功能选项:

在这里插入图片描述
在这里插入图片描述

-9选项,可以杀掉一个进程,效果和ctrl c中断进程是一样的,用法是kill -9 进程pid

在这里插入图片描述
在这里插入图片描述

而这种D状态,是无法被中断的,只能等待硬件的操作完成才会退出这种状态!

我们发现ps查询进程状态时,状态字母后会带一个+号,这代表着这个进程是前台进程,没有+则是后台进程。前台进程同一时间只能有一个,后台进程可以有多个。只有前台进程能获取键盘数据,所以输入ctrl c只能中断前台进程,中断后台进程只能用kill -9命令。执行一个命令后加&,则指定这个进程是后台进程。

3. 暂停状态(stopped)和追踪暂停状态(tracing stop)

kill -19 进程pid可以暂停一个进程,kill -18 进程pid可以取消暂停

演示kill发送信号暂停进程

在这里插入图片描述
在这里插入图片描述

进程暂停后恢复,发现变成了后台进程。此时想中断进程只能用kill -9了。

正常暂停是T状态,还有一种追踪暂停状态t,这其实是gdb背景下追踪进程,调试的时候会显示这种状态:

在这里插入图片描述
在这里插入图片描述

4. 僵尸状态(zombie)、死亡状态(dead)

我们创建一个进程,本质都是要让他完成某种任务的。当一个进程运行结束后,不应该直接挂掉,而是需要OS或父进程检查进程的返回信息,判断任务是否完成,此时这个进程就是僵尸状态。而检查完毕后,资源确定可以释放,进程才能变成死亡状态并被回收。

  • X:死亡状态。这个状态是瞬时的,我们看不到。也不用过多关注
  • Z:僵尸状态

僵尸状态是一个比较特殊的状态。当进程退出并且父进程没有读取到子进程退出的返回信息时,就会产生僵尸进程,僵尸状态会以终止状态保持在进程表中,并且会一直等待父进程读取退出状态代码。也就是说,只要子进程退出,父进程还在运行,但父进程没有读取到子进程的状态,子进程就进入僵尸状态。

进程退出时,退出信息是main函数返回值 或 收到的信号值退出信息会保存在自己的task_struct中,需要OS或父进程检查后才能释放。命令行启动的进程退出时,会由-bash自动检查回收,而我们自己的程序中如果fork了子进程,如果强行中断子进程后,父进程也不回收,子进程就会处在Z状态:

在这里插入图片描述
在这里插入图片描述

父进程可以使用系统调用waitwaitpid等来回收子进程。

子进程必须回收!如果不回收,子进程会一直处于僵尸状态,导致他的task_struct无法释放,就造成了内存泄漏!进程正常结束了,内存泄漏也不存在了。

5. 孤儿进程

如果父进程提前退出了,那么子进程退出变成僵尸进程后该怎么办呢?此时这个子进程就称为孤儿进程

演示:

在这里插入图片描述
在这里插入图片描述

结论:

  • 孤儿进程,会自动变成后台进程
  • 父进程退出后,孤儿进程的ppid变成了1,也就是被1号进程init(或systemd)“领养”
在这里插入图片描述
在这里插入图片描述

1号进程是系统初始化进程,这样一来,孤儿进程都由系统接管,孤儿进程结束后系统就会自动回收它们,不用担心它们变成僵尸进程了。

6. 挂起状态

进程的task_struct存在于内存中,当内存空间资源不足时,一些进程对应的代码和数据会交换到磁盘上的swap分区,此时进程称为挂起状态。代码和数据换入swap分区,称为swap in,换出则称为swap out。 但是swap分区是在磁盘上的,换入换出涉及到内存和磁盘的IO操作,时间就比内存内的数据流动慢了很多,但将数据换到swap分区目的也是腾出内存空间。swap分区不建议过大,过度的swap会使系统变慢,这一过程本质就是用时间换空间。

如果内存资源还是不足,系统会使阻塞状态S的进程也挂起到swap分区,称之为“阻塞挂起”。 如果内存资源再不足,系统会使当前CPU运行队列中,不是正在运行进程的其他进程也挂起,轮到他运行时再换出,称之为“运行挂起”。 如果内存资源依旧不足,Linux系统就会选择性地杀掉部分进程!

本篇完,感谢阅读!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-11-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 进程状态
    • 1. 运行状态(running)与睡眠状态(sleeping)
    • 2. 深度睡眠状态(disk sleep)
    • 3. 暂停状态(stopped)和追踪暂停状态(tracing stop)
    • 4. 僵尸状态(zombie)、死亡状态(dead)
    • 5. 孤儿进程
    • 6. 挂起状态
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档