[1. 进程状态](#1. 进程状态)
[1.1 操作系统科学 的 进程状态](#1.1 操作系统科学 的 进程状态)
[1.11 运行态 & 就绪态 ------ 就绪未出队,等待运行。运行已出队,正在运行](#1.11 运行态 & 就绪态 —— 就绪未出队,等待运行。运行已出队,正在运行)
[1.12 阻塞态 ------ 卡住 就去 卡住的软硬件 的 等待队列 等待资源](#1.12 阻塞态 —— 卡住 就去 卡住的软硬件 的 等待队列 等待资源)
[1.13 挂起态 ------ 与磁盘进行大量IO操作,以时间换空间,卡顿的原因](#1.13 挂起态 —— 与磁盘进行大量IO操作,以时间换空间,卡顿的原因)
[1.2 调度队列是啥? ------ Linux内核双链表的实现方式](#1.2 调度队列是啥? —— Linux内核双链表的实现方式)
[1.3 Linux内核 根据偏移量求地址的方式](#1.3 Linux内核 根据偏移量求地址的方式)
[1.4 这么麻烦,为了? ------ 链表节点 可改造成其他 数据结构,并且都具备通用性!](#1.4 这么麻烦,为了? —— 链表节点 可改造成其他 数据结构,并且都具备通用性!)
[1.5 Linux 操作系统 的 进程状态](#1.5 Linux 操作系统 的 进程状态)
[1.51 R(running)运行态 ------ 在调度队列中就是运行态](#1.51 R(running)运行态 —— 在调度队列中就是运行态)
[1.511 ps axj ------ 可观察进程属性。搭配grep行过滤关键词](#1.511 ps axj —— 可观察进程属性。搭配grep行过滤关键词)
[1.512 查看循环printf进程,总是S,极少R? ------ CPU快,屏幕慢,printf频繁在等待屏幕输出](#1.512 查看循环printf进程,总是S,极少R? —— CPU快,屏幕慢,printf频繁在等待屏幕输出)
[1.513 查看状态,总是有+或s ------ [ ]+是前台进程,[ ]是后台进程,s是登录shell会话首进程](#1.513 查看状态,总是有+或s —— [ ]+是前台进程,[ ]是后台进程,s是登录shell会话首进程)
[1.514 ./myproc & ------ 加 & 在结尾,就能在后台启动进程!无法ctrl + c杀死!](#1.514 ./myproc & —— 加 & 在结尾,就能在后台启动进程!无法ctrl + c杀死!)
[1.515 kill -9 [进程pid] ------ 杀死后台进程的方式](#1.515 kill -9 [进程pid] —— 杀死后台进程的方式)
[1.516 前台,后台? ------ 定义读取键盘的优先级是前台 + 多进程并行执行的效率提高](#1.516 前台,后台? —— 定义读取键盘的优先级是前台 + 多进程并行执行的效率提高)
[1.517 kill -l ------ 查看kill命令的全部选项中:9,18,19](#1.517 kill -l —— 查看kill命令的全部选项中:9,18,19)
[1.52 S(sleeping)可中断睡眠态 ------ 阻塞态,浅睡眠态(OS、指令随便杀)](#1.52 S(sleeping)可中断睡眠态 —— 阻塞态,浅睡眠态(OS、指令随便杀))
[1.53 T (stopped)停止态 ------ 抢前台资源,被OS强行休眠,阻塞态](#1.53 T (stopped)停止态 —— 抢前台资源,被OS强行休眠,阻塞态)
[1.530 jobs [-l] ------ 可查看后台进程的编号,pid,状态等](#1.530 jobs [-l] —— 可查看后台进程的编号,pid,状态等)
[1.530 fg [后台进程编号] ------ 将后台进程,拉回前台执行](#1.530 fg [后台进程编号] —— 将后台进程,拉回前台执行)
[1.531 kill -19 [pid] ------ stop,前台循环进程进入T,默认切换至后台进程](#1.531 kill -19 [pid] —— stop,前台循环进程进入T,默认切换至后台进程)
[1.532 kill -18 [pid] ------ 唤醒后台进程,会继续执行,但不会回到前台](#1.532 kill -18 [pid] —— 唤醒后台进程,会继续执行,但不会回到前台)
[1.54 t (tracing stop)追踪停止态 ------ 调试时,遇到断点,被调试器强行休眠](#1.54 t (tracing stop)追踪停止态 —— 调试时,遇到断点,被调试器强行休眠)
[1.541 cgdb是一个进程](#1.541 cgdb是一个进程)
[1.542 cgdb调试程序本质是打开了指定路径的可执行程序](#1.542 cgdb调试程序本质是打开了指定路径的可执行程序)
[1.543 调试器是父进程,运行的程序是子进程](#1.543 调试器是父进程,运行的程序是子进程)
[1.55 D(disk sleep)不可中断睡眠态 ------ 深睡眠态,阻塞态(无法杀,除了断电)](#1.55 D(disk sleep)不可中断睡眠态 —— 深睡眠态,阻塞态(无法杀,除了断电))
[1.56 Z(zombie)僵尸态 ------ 子进程结束,一定变成僵尸进程,等待父进程善后](#1.56 Z(zombie)僵尸态 —— 子进程结束,一定变成僵尸进程,等待父进程善后)
[1.561 孤儿进程 ------ 父进程先 Z,子进程都过继给systemd pid = 1:OS本身善后。孤儿进程默认被切换为后台进程](#1.561 孤儿进程 —— 父进程先 Z,子进程都过继给systemd pid = 1:OS本身善后。孤儿进程默认被切换为后台进程)
[1.57 X(dead)死亡态 ------ 由 Z 改为 X,是瞬时状态,无法查看](#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才会被彻底销毁



&x &(x.a) 完全一样!




























