关于进程状态

发布于:2025-04-21 ⋅ 阅读:(15) ⋅ 点赞:(0)

目录

进程的各种状态

运行状态

阻塞状态

挂起状态

linux中的进程状态、

进程状态查看

S状态(浅睡眠)

t 状态(追踪状态)

T状态(暂停状态)

​编辑

kill命令手册

D状态(深度睡眠)

Z状态(僵尸状态)

孤儿状态


我们知道cpu是通过一个调度队列来处理调度一个个进程的,一个cpu,一个调度队列!

一个个进程肯定有多种不同的状态,到底是执行完了还是没有,或者是一直执行某个语句不动的状态,所以我们现在详细讲讲。

进程的各种状态

运行状态

运行状态比较好理解,我们说只要进程在调度队列中就都是运行状态(running)。

为什么同一个节点可以在不同的数据结构中呢?怎么实现的?

 我们之前在学习数据结构的时候,同一个节点只能在一个数据结构中,而OS中却不是这样的,它可以在不同的数据结构中。

有了list_head,我们只需要管理list_head就能间接管理进程pcb。

我们可以在task_struct 中设计多个list_head结构体,这样多个数据结构容器只需要对各自的list_head结构体管理就能间接管理task_struct:

阻塞状态

阻塞状态:等待某种设备(键盘,显示器,网卡,磁盘,摄像头......)或者资源的就绪。

我们最常接触到的阻塞就是程序运行在scanf语句的时候,等待键盘的输入,说白了就是在等待键盘就绪,这时候进程从运行状态变为阻塞状态。

更深理解:

我们知道OS管理硬件也是和进程一样一个个结构体描述硬件,在每一个硬件中其实也有一个队列,叫等待队列。程序刚开始在运行的时候是运行状态,在执行到sacnf语句时,这个过程,进程pcb从调度队列链出,链入键盘的等待队列。此时进程就是阻塞状态。

当我们输入完毕,键盘就绪,OS判断等待队列是否为空,不为空将这个进程pcb就会重新链出等待队列,链入调度队列,变为运行状态。

设备管理:

挂起状态

磁盘中有一个swap交换分区(大小是内存的1.5倍或2倍等),当内存资源严重不足的情况下,OS将阻塞进程的代码和数据唤入swap交换分区,此时这些进程状态就叫阻塞挂起状态。当这些进程要被调度时,将这些进程的代码和数据再唤入内存中。有时甚至会将调度队列中的末尾进程唤入swap交换分区。

图:

linux中的进程状态、

进程状态查看

 ps aux / ps axj  命令
  • a:显⽰⼀个终端所有的进程,包括其他⽤⼾的进程。
  • x:显⽰没有控制终端的进程,例如后台运⾏的守护进程。
  • j:显⽰进程归属的进程组ID、会话ID、⽗进程ID,以及与作业控制相关的信息
  • u:以⽤⼾为中⼼的格式显⽰进程信息,提供进程的详细信息,如⽤⼾、CPU和内存使⽤情况等

在linux内核中,linux状态和以上的状态有所不同,上面只是适合所有操作系统,但是不同操作系统之间还是有差别的。

在每个task_struct中都有一个变量记录一下进程的状态,上图是一个状态数组,而一般task_struct中的这个变量其实就是一个整数(每个不同的整数代表不同的状态)。

  • R ----运行状态
  • S ----浅睡眠状态(可中断睡眠状态)
  • D ----深睡眠状态(不可中断睡眠状态)
  • S和D其实都属阻塞状态
  • t ----追踪状态
  • T ----暂停状态
  • X ----死亡状态
  • Z----僵尸状态

我们看看具体概念:

  • R运⾏状态(running):并不意味着进程⼀定在运⾏中,它表明进程要么是在运⾏中要么在运⾏ 队列⾥。
  • S睡眠状态(sleeping):意味着进程在等待事件完成(这⾥的睡眠有时候也叫做可中断睡眠 (interruptible sleep))。
  • D磁盘休眠状态(Disksleep)有时候也叫不可中断睡眠状态(uninterruptiblesleep),在这个 状态的进程通常会等待IO的结束。
  • T停⽌状态(stopped):可以通过发送SIGSTOP信号给进程来停⽌(T)进程。这个被暂停的 进程可以通过发送SIGCONT信号让进程继续运⾏。
  • X死亡状态(dead):这个状态只是⼀个返回状态,你不会在任务列表⾥看到这个状态。

我们一个个来讲解!!!

S状态(浅睡眠)

我们可以换成后台输入运行,此时我们的命令行可以继续输入其他命令:

t 状态(追踪状态)

当一个程序被debug的时候,就是一个追踪状态。

T状态(暂停状态)

kill命令手册

kill有很多命令,我们可以查:

其中:

  • -9 :杀掉进程
  • -18:恢复进程
  • -19:暂停进程

D状态(深度睡眠)

首先为什么要有D状态呢?

看一个情景:

某个进程,要将100MB数据写入磁盘,此时进程状态是S状态(等待数据全部写入磁盘,写完之后会告诉进程成功与否(返回值实现)),如果此时内存空间严重不足,我们知道OS会将一些阻塞进程甚至调度队列末尾进程挂起,可是这样仍然不足呢?那么OS很有可能将这个写入磁盘进程杀掉,这个进程杀掉了,那这100MB数据怎么办?如果写入磁盘时,磁盘空间也不足,写入失败了,本来要返回告诉这个进程失败信息,但是此时进程被杀掉了,也就是说现在用户也不知道这100MB数据写入失败了,这100MB数据就丢失了!如果这100MB数据是某个银行转账一天的流水呢!

所有说才会有D状态进程,不可被OS杀掉,这样就算写入失败了,用户也知道失败了!

一般在高IO流的时候才会出现!

Z状态(僵尸状态)

只要是进程,那么它一定有父进程,而当子进程运行结束,子进程的相关信息是需要被父进程获取的,而我们知道进程的相关信息是在它的pcb的,也就是说子进程运行完,此时OS可以将它的代码和数据释放掉,但是pcb不能释放掉,父进程获取完子进程信息之后,子进程正式退出!

子进程运行完之后,父进程获取子进程相关信息,子进程正式退出之前,这就是僵尸状态!

看代码:

看现象:

如果父进程一直不管,一直不回收子进程的pcb,那么子进程一直都是僵尸状态,子进程的pcb一直就得不到释放,这会导致内存泄露。

那进程内存泄露了,进程退出了,内存泄露还存不存在?

不存在。

就像之前我们学习c语言的时候,一个main函数里面,死循环开辟内存,而不释放,就会内存泄漏,而当程序结束完(return 0后),OS就自行回收内存!

那什么样的进程害怕内存泄漏呢?

我们刚刚说的那个显然是不害怕内存泄漏的,一些常驻内存的进程就会害怕内存泄漏。

常驻内存的进程就是那些启动之后不退出的,一旦启动不退出的进程。

比如操作系统就是一个启动不退出的进程,如果操作系统内核代码出现了内存泄漏,就会越来越卡。

task_struct的节点是怎么申请和释放的?

在平常的使用当中,大部分都是多进程并发的,那肯定离不开一个个的pcb去不断申请和释放,但却不是我们常认识的那种申请和释放。

有一个unuse区域,专门存储那些要释放的task_struct节点,当我们要释放某个task_struct的时候,将这个节点放入unuse区域即可,当我们要申请一个新的节点的时候,我们只需要在unuse区域中拿即可!

孤儿状态

父子进程关系中,如果父进程先退出,子进程要被1号进程领养,这个子进程就叫作孤儿进程。

这1号进程其实就是操作系统!既然被1号进程领养自然要被1号进程回收!

看代码:

现象:

子进程被领养之后就变成后台进程了!

看看1号进程:

我们可以看到,这个1号进程是叫systemd(老版本叫init)进程,它其实也有一个0号进程,但我们一开机这个0号进程就被1号进程取代了,这个我们不详细谈。

为什么1号进程需要领养呢?

前面知道,子进程需要被父进程获取信息(回收),这个时候子进程是僵尸状态,但是此时子进程没有父进程了,那么这个子进程就会内存泄漏,所以需要被领养,最后统一回收!

好了,我们下期见!


网站公告

今日签到

点亮在社区的每一天
去签到