< 9 > Linux 进程:进程状态 + 进程切换 + 附带常用指令(jobs / fg / kill / ps)
操作系统进程状态分为运行态、就绪态、阻塞态和挂起态。运行态指进程占用CPU执行;就绪态指进程在调度队列等待CPU分配;阻塞态指进程因等待资源而暂停执行;挂起态则是内存不足时将进程数据换出到磁盘。Linux内核采用双链表实现调度队列,通过偏移量计算地址实现通用性。Linux特有进程状态包括:R、S、T、D、Z等。前台进程占用键盘资源,后台进程并行执行提高效率。僵尸进程需父进程回收,孤儿进程由syst
1.11 运行态 & 就绪态 —— 就绪未出队,等待运行。运行已出队,正在运行
1.12 阻塞态 —— 卡住 就去 卡住的软硬件 的 等待队列 等待资源
1.13 挂起态 —— 与磁盘进行大量IO操作,以时间换空间,卡顿的原因
1.2 调度队列是啥? —— Linux内核双链表的实现方式
1.4 这么麻烦,为了? —— 链表节点 可改造成其他 数据结构,并且都具备通用性!
1.51 R(running)运行态 —— 在调度队列中就是运行态
1.511 ps axj —— 可观察进程属性。搭配grep行过滤关键词
1.512 查看循环printf进程,总是S,极少R? —— CPU快,屏幕慢,printf频繁在等待屏幕输出
1.513 查看状态,总是有+或s —— [ ]+是前台进程,[ ]是后台进程,s是登录shell会话首进程
1.514 ./myproc & —— 加 & 在结尾,就能在后台启动进程!无法ctrl + c杀死!
1.515 kill -9 [进程pid] —— 杀死后台进程的方式
1.516 前台,后台? —— 定义读取键盘的优先级是前台 + 多进程并行执行的效率提高
1.517 kill -l —— 查看kill命令的全部选项中:9,18,19
1.52 S(sleeping)可中断睡眠态 —— 阻塞态,浅睡眠态(OS、指令随便杀)
1.53 T (stopped)停止态 —— 抢前台资源,被OS强行休眠,阻塞态
1.530 jobs [-l] —— 可查看后台进程的编号,pid,状态等
1.530 fg [后台进程编号] —— 将后台进程,拉回前台执行
1.531 kill -19 [pid] —— stop,前台循环进程进入T,默认切换至后台进程
1.532 kill -18 [pid] —— 唤醒后台进程,会继续执行,但不会回到前台
1.54 t (tracing stop)追踪停止态 —— 调试时,遇到断点,被调试器强行休眠
1.542 cgdb调试程序本质是打开了指定路径的可执行程序
1.55 D(disk sleep)不可中断睡眠态 —— 深睡眠态,阻塞态(无法杀,除了断电)
1.56 Z(zombie)僵尸态 —— 子进程结束,一定变成僵尸进程,等待父进程善后
1.561 孤儿进程 —— 父进程先 Z,子进程都过继给systemd pid = 1:OS本身善后。孤儿进程默认被切换为后台进程
1.57 X(dead)死亡态 —— 由 Z 改为 X,是瞬时状态,无法查看
1. 进程状态
1.1 操作系统科学 的 进程状态
操作系统科学 的 进程状态 适用于 大部分操作系统
进程状态大类分3种:运行,阻塞,挂起
进程状态看起来遥不可及,实际上就是每个PCB中的整型变量 int state
只不过根据状态标记的不同,需要的操作,和导致的结果不同罢了。下面细说
1.11 运行态 & 就绪态 —— 就绪未出队,等待运行。运行已出队,正在运行
就绪态:进程处在调度队列中,按照FIFO,等待分配CPU(还未出队)
运行态:进程从调度队列中,被调度选中、出队、正式占用CPU开始执行(已经出队)
1.12 阻塞态 —— 卡住 就去 卡住的软硬件 的 等待队列 等待资源
阻塞态:进程 主动/被动 放弃 占用/等待 CPU,暂时不再参与调度,直到等待的资源获取成功,才会被唤醒
原理:进程处于运行态,正在占用CPU执行进程时,分两种情况阻塞态:
后台进程:强行读取抢占前台资源,如shell正在使用的键盘,被认为是插队,OS将其休眠,然后将该节点直接从 CPU 插入到 键盘硬件 的 等待队列 中,等待资源,进入阻塞态
前台进程:因为等待scanf读取键盘的输入资源,CPU调用库函数调用scanf,其封装了read()系统调用,CPU执行到系统调用发现没有读取到资源的输入,就会申请OS执行休眠函数,OS执行休眠函数,然后将该节点直接从CPU 插入 键盘硬件 的 等待队列 中,等待资源,进入阻塞态
1.13 挂起态 —— 与磁盘进行大量IO操作,以时间换空间,卡顿的原因
挂起态:因为进程多且大,导致内存空间不足时,OS会将优先级较低的进程:就绪态、阻塞态 在内存中的代码段和数据段换入磁盘的swap分区空间,空留下PCB节点排队等待调度运行,这称为挂起状态。轮到他们运行时,会从磁盘拷贝回来,再运行。
细分:阻塞时挂起,是阻塞挂起态,就绪时挂起,是就绪挂起态
原理:
所有操作系统,一般 都会 给磁盘空间预留一段 swap分区
当内存空间不足时,OS为了提高空间,会将暂时运行不上的进程:就绪态、阻塞态的进程,将他们的在内存中 代码段 和 数据段 移动拷贝 到 磁盘的swap分区,扩大内存空间。PCB节点则不移动,就在原地等候排队调度。
缺陷:
磁盘的IO速度与CPU不是一个量级,速度差巨慢,这也是为什么进程越多,电脑/手机 越卡顿的原因:内存空间严重不足,需要与磁盘进行大量的IO操作来回切换数据,效率巨慢
1.2 调度队列是啥? —— Linux内核双链表的实现方式
调度队列本质上是双链表,不过只是用了头尾插入删除操作
链表可以根据使用方式,改造成几乎任何常用的高级数据结构
Linux内核的链表,不是将每个PCB作为链表节点参数之一
而是:PCB内部内嵌链表节点,节点参数就两个:next,prev
为什么这么做? 原因:高度解耦、通用的泛型链表!
原思路链表:链表节点内嵌PCB,
缺陷:PCB参数改动,或者节点新增参数,所有链表节点立刻失效,都得跟着改!耦合度极高,不好维护
Linux内核链表思路:PCB内嵌链表节点
优点:PCB改变参数,都可通过链表节点,再通过求取偏移量的方式(后面),得到变量。
每个PCB都可以自定义变量,怎么样都能找到变量,而这又和链表无关:链表只负责链接和提供自己地址。耦合度极低,更好维护
1.3 Linux内核 根据偏移量求地址的方式
C/C++ : &a[0] 和 &a 的结果,在数字上,完全一致
都是数组的首地址。换作结构体:
&x &(x.a) 完全一样!
都是结构体的首地址,得出地址后,根据结构体对齐原则,算出变量大小,加上偏移量,就能求出其他变量的地址!
基于此原理:假设0号地址存在结构体对象类型,直接强转(struct obj*)0,访问d元素,求偏移量:
既然offset是起始地址 - d的地址偏移量,那做个实验比较一下:
结论Linux内核中,可这样:
知道节点地址 -> (PCB*)0 -> 箭头访问节点 -> 取地址 -> 得出节点偏移量->反推真正PCB首地址
这样,PCB地址就得到了!想访问其他变量一样:直到变量名,重复上面过程!
1.4 这么麻烦,为了? —— 链表节点 可改造成其他 数据结构,并且都具备通用性!
只需要改变链表的使用方式,就能以一个链表节点结构体,创建出几乎所有常用高级数据结构!并且也与链表节点本身一样,具备极强通用性!所有数据结构的源码就2行:
listnode* next ,listnode* prev!
1.5 Linux 操作系统 的 进程状态
1.51 R(running)运行态 —— 在调度队列中就是运行态
1.511 ps axj —— 可观察进程属性。搭配grep行过滤关键词
运行态:在调度队列中,就是运行态,这点 与 操作系统科学 的 概念不同。
严格来讲,需要区分 就绪态 和 运行态。
1.512 查看循环printf进程,总是S,极少R? —— CPU快,屏幕慢,printf频繁在等待屏幕输出
利用ps axj 命令,观察进程运行的状态:
为啥一会是S(sleeping,浅睡眠态,阻塞态),一会又是R(running,运行态)?
按理来说,应该是一直在运行态?
原因:进程看似一直在运行,实则是屏幕设备的 IO速度太慢!
CPU跑代码飞快,但终端屏幕设备 IO 极慢,进程绝大部分时候都在等屏幕设备刷新!
所以就大部分时候都在浅睡眠的阻塞态
运气好,疯狂ps,可能发现它在R状态!但绝对是S状态更多
1.513 查看状态,总是有+或s —— [ ]+是前台进程,[ ]是后台进程,s是登录shell会话首进程
正常启动启动进程,就是前台进程,状态总是带 +
前台进程,占据了前台资源,bash指令无法使用!
下面演示,后台进程如何运行:
1.514 ./myproc & —— 加 & 在结尾,就能在后台启动进程!无法ctrl + c杀死!
1.515 kill -9 [进程pid] —— 杀死后台进程的方式
杀死了。
1.516 前台,后台? —— 定义读取键盘的优先级是前台 + 多进程并行执行的效率提高
多个进程,需要执行,如果都需要读取键盘文件,键盘只能由一个进程使用。
那肯定要按先后顺序提供读取
所以:先读取的,定义为前台进程。后读取的,称为后台进程。
前台进程只有一个,后台进程可以有很多个。因为目前都用不到键盘文件
效率提高:
不需要键盘的进程,统一在后台跑,前台只处理需要键盘的:如shell指令
这样:多进程同时执行,效率提升飞快!
如果只有前台:
输完指令,还得单独启动输出信息、磁盘IO文件等进程,效率低。
前台,后台:
不需要键盘的一律在后台执行!如果100个不需要,就节省100个操作运行时间!
1.517 kill -l —— 查看kill命令的全部选项中:9,18,19
重点学习 9,18,19:杀死,继续,停止
SIG:kill CONT:continue STOP:stop
效果等价 kill -9 PID kill -SIGKILL PID
1.52 S(sleeping)可中断睡眠态 —— 阻塞态,浅睡眠态(OS、指令随便杀)
可中断睡眠态:主动 向OS申请休眠,从CPU离开 或 从调度队列出队,入队具体硬件的等待队列。本质是阻塞态
可中断:内存严重不足,OS可随便杀死进程,kill指令、crtl + c 也能随便杀进程
1.53 T (stopped)停止态 —— 抢前台资源,被OS强行休眠,阻塞态
停止态:被动 休眠,因为抢占前台进程资源,被OS强行休眠,从就绪态,变为停止态:
PCB结构体节点被OS从调度队列出队,入队具体硬件的等待队列。本质也是阻塞态
1.530 jobs [-l] —— 可查看后台进程的编号,pid,状态等
1.530 fg [后台进程编号] —— 将后台进程,拉回前台执行
1.531 kill -19 [pid] —— stop,前台循环进程进入T,默认切换至后台进程
前台如果在跑循环打印进程,kill -19 [pid] : 强制该进程T状态
因为前台不允许阻塞态,bash会自动取得操作权,把该进程由前台转移至后台
1.532 kill -18 [pid] —— 唤醒后台进程,会继续执行,但不会回到前台
1.54 t (tracing stop)追踪停止态 —— 调试时,遇到断点,被调试器强行休眠
追踪停止态:被动停止。debug版本exe,用调试器打断点调试时,运行到断点,进程就会被调试器强行休眠暂停,是阻塞态
1.541 cgdb是一个进程
1.542 cgdb调试程序本质是打开了指定路径的可执行程序
先区分:
cgdb:封装gdb,套的外壳
gdb --nw ... :真正的调试器,cgdb封装gdb整的可视化调试界面
1.543 调试器是父进程,运行的程序是子进程
如图:
cgdb是父进程,gdb是cgbd的子进程,同时是myproc的父进程!
1.55 D(disk sleep)不可中断睡眠态 —— 深睡眠态,阻塞态(无法杀,除了断电)
不可中断睡眠态:与磁盘进行大量 IO 操作,可能涉及重要内容的交互,或者说为了保护读写安全,CPU执行时会申请OS标记为D:不允许被任何形式杀死,也不允许被swap挂起。断电能杀死。
允许从CPU离开、插入 IO 等待队列
一般情况都是S状态。极少情况是D状态
除非:与磁盘进行大量 IO 操作,磁盘老旧,读写故障率高,读写慢
涉及底层硬件的写入..
1.56 Z(zombie)僵尸态 —— 子进程结束,一定变成僵尸进程,等待父进程善后
僵尸态:子进程结束后,它的代码段与数据段会立刻销毁。PCB不会彻底销毁:它会保留极少数状态信息如pid,state,子进程结束退出时,触发系统调用exit(),OS修改 state 为 Z。
此时就是僵尸状态:已经退出,但还没人读取的状态。
PCB在等父进程来读取。如果父进程早死了,无法读取,就会内存泄漏,变成孤儿进程!
为了解决内存泄漏,所有孤儿进程都会被过继给pid = 1 的OS内核大管家:systemd
如图:前台的僵尸进程!并且:会被标记 <defunct> 失效的,无用的!
1.561 孤儿进程 —— 父进程先 Z,子进程都过继给systemd pid = 1:OS本身善后。孤儿进程默认被切换为后台进程
孤儿进程默认被切换为后台进程
1.57 X(dead)死亡态 —— 由 Z 改为 X,是瞬时状态,无法查看
死亡态:子进程由 僵尸态 被 父进程 通过 系统调用wait(),被 OS 回收,标记为X,此时进程才算彻底结束,PCB才会被彻底销毁
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐



































所有评论(0)