目录
[D : 磁盘休眠态(深度休眠)](#D : 磁盘休眠态(深度休眠))
[X :终止态](#X :终止态)
[Z :🧟♀️僵尸状态](#Z :🧟♀️僵尸状态)
一、Linux中的进程状态
进程在Linux中有以下几个状态
- R (running)
- S (sleeping)
- D (disk sleep)
- T (stopped)
- t (tracing stop)
- X (dead)
- Z (zombie)
R:运行态
表明进程要么是在运⾏中要么在运⾏队列⾥
演示一下,依旧makefile

mycode.c:

运行一下

R+ : 表示这个进程在前台运行,此时我们的bash就不运行了。
bash
./mycode &
表示在后台运行,此时用 ps ajx | grep mycode

就显示 R 在后台运行,此时bash 依旧能相应我们的指令

kill -9 12183 只能这样杀掉进程了。
S:休眠态(浅度休眠)
在操作系统中有一种状态叫阻塞态,要等待资源的就绪才能运行的状态
Linux中的休眠态就是阻塞态。
D : 磁盘休眠态(深度休眠)
浅度休眠可以被唤醒也可以被kill (杀死)。
假如在内存中有一个进程向磁盘中做写入1GB数据,磁盘把数据拷贝到某个地方是要花时间的,此时这个进程就必须要在这里等。如果此时好巧不巧,内存资源严重不足,操作系统把这个进程都挂起了,还是不行,进程没有什么情况说明的话,操作系统会把这个进程干掉(操作系统是会杀进程的,比如windows中的闪退),此时磁盘的数据还没写成功,磁盘可能就直接把数据丢失了。
为了避免这种情况,这种进程就要向外界传达信息:任何人不能杀掉我这个进程,于是就有了D这种状态:不响应任何请求。
当然,当出现D这种状态的进程时,操作系统就已经要挂了。
同样Linux中的D也是阻塞态。
挂起状态
挂起状态在Linux中,用户是不可见的,由操作系统一手来操作的,用户不可知。
T:暂停状态
在 LInux当中,我们可以主动的暂停一个进程
bash
kill -l

bash
kill -19 PID 暂停一个进程


bash
kill -18 cont (continue) 继续一个进程
Stop状态与Sleep状态的区别:
T : 不一定是硬件上的资源的等待,包含了主观要求的等待
S : 是由于硬件上的等待
T : 也是一种阻塞状态。
T 状态的使用场景 : 代码的调试
X :终止态
这种进程会被放到一个垃圾回收的队列当中,操作系统就可以定期的把这个队列中的占用资源释放掉
Z :🧟♀️僵尸状态
当一个进程死亡的时候,它并不会立即进入dead的状态,也就是一个进程运行完了,它不会立即被放到垃圾♻️回收队列当中,而是先进入一个状态叫Z状态。
为什么 :
一个进程运行完了是要被检测的,要清楚这个进程当前的信息,还要把结果返回给它的父进程。
在这个,进程结束了,要等待父进程来关系这个进程,的过程中,这个进程的状态要标记为Z/僵尸状态。
进程一般退出的时候,如果父进程没有主动回收♻️子进程的信息,子进程会一直让自己处于Z状态,进程的相关资源尤其是task_struct 结构体不能被释放。
如果一个进程的资源一直不退,会导致内存泄露。
如果一个子进程在运行,但父进程掉了,子进程会有什么影响。
子进程的父进程会变成操作系统,此时这个子进程被称为孤儿进程。
:父子进程,父进程先退出,子进程的父进程会被改成1号进程(操作系统)父进程是1号进程----孤儿进程
该进程被系统领养!
一为什么要被领养???因为孤儿进程未来也会退出,也要被释放
二、Linux中的进程优先级
每个进程都要排队等待CPU的调度和执行,这里的排队就是进程之间的优先级。
进程和进程之间不是按固定的一个链表链接的。
想把一个进程放到哪个数据结构中去,就创建哪个数据结构的对象放到该进程的PCB中

把这个数据结构放到该进程的PCB中,这个数据结构也许是哈希表,或者链表,多叉树......., 只要有了这个数据结构,通过图示的方法,就能拿到该PCB的地址,就能访问PCB中的数据了。
在Linux中是怎么安排进程的优先级呢?
ps -l 是 Linux/Unix 系统中用于查看当前用户进程的常用命令,它会以长格式(Long Format)显示更详细的进程信息。
ps -l 是你排查"进程为什么卡住"、"谁占用了太多资源"时的首选工具。

| 字段 | 含义 | 说明 |
|---|---|---|
| F | Flags | 进程标志(常为 0/4,4 表示 root 权限) |
| S | STAT | 进程状态(最重要,如 R=运行, S=睡眠, Z=僵尸) |
| UID | User ID | 进程所有者的用户 ID |
| PID | Process ID | 进程唯一标识符 |
| PPID | Parent PID | 父进程 ID |
| PRI | Priority | 进程优先级(数值越小优先级越高) |
| NI | Nice Value | 友好值(用于调整优先级,-20 到 19) |
| SZ | Size | 占用内存大小(KB) |
| TTY | Terminal | 关联的终端设备 |
| TIME | CPU Time | 累计占用 CPU 时间 |
| CMD | Command | 启动进程的命令名 |
我们主要来看PRI ,NI 。
PRI的值越小越快被执行,PRI (new) = 80 + nice
所以调整进程的优先级,在Linux下,就是调整nice值。
nice 其取值范围是-20~19,可以把nice值当做优先级的修正值。
普通用户是不能更改的
操作系统是如何根据优先级进行调度的呢
优化后的文本:
系统维护两个指针数组:运行指针数组和等待指针数组。运行指针(二级指针)指向运行指针数组,等待指针(二级指针)指向等待指针数组。
由于进程优先级可能相同,相同优先级的进程的PCB会被链入同一个运行队列,不同优先级的进程则分别链入不同的运行队列。系统将预先划分好的40个不同优先级运行队列(同一个优先级的PCB被分在同一个小队列中)的头指针存入运行指针数组中。基于Linux内核的O(1)调度算法,通过位图机制判断指针数组中哪些位置的运行队列尚未执行,然后按优先级顺序依次执行这些队列。
我们的调度优先级就是开散列哈西表的优先级,在task_struct* runqueue[]数组中的下标就是某一小队列的优先级
每个进程都分配有时间片,当进程运行时间达到时间片设定值时,会被重新链入当前运行队列的末尾排队等待。
在执行过程中,若有新进程需要运行,为避免影响当前按优先级划分的运行队列的执行,这些新进程会被链入等待队列。等待队列同样按优先级划分,其头指针存入等待指针数组中。
当运行指针数组中的所有运行队列都执行完毕后,等待指针数组中已按优先级划分的运行队列就绪待执行。此时交换两个指针数组的角色(swap(&run,&wait)):运行指针指向原等待指针数组,等待指针指向原运行指针数组。这样原等待指针数组成为新的运行指针数组,原运行指针数组则转为等待指针数组。通过这种轮换机制,系统可以持续运行进程,同时保持进程优先级调度不受影响。
所有在runqueue中的PCB的状态都是R,它们会根据优先级被分散到不同的小队列中(task_struct * 数组中的不同下标数,依据数组下标的不同,我们从上到下访问PCB,就是按照优先级遍历。
调整优先级的本质就是把PCB链入到不同数组下标的队列中。
位置不同访问的优先级不同。