[Linux] 进程状态相关概念、Linux实际进程状态、进程优先级
进程状态的概念
-
进程 = PCB + 程序代码、数据
-
操作系统会对进程生成PCB这样描述进程所有属性的结构体, 而为了方便管理,
操作系统也对所有硬件
, 例如:CPU、磁盘、显卡、声卡、网卡……描述了他们的所有属性, 生成类似PCB这样的结构体 用于对硬件设施的管理
。即, PCB是描述进程所有属性的结构体, 而操作系统中也存在 描述CPU、磁盘、显卡等硬件的所有属性的结构体 -
在描述硬件属性的结构体中, 一般都会存在一个用于给进程分配资源所存在的队列, 当
进程需要某种资源时, 如需要在显示器上显示内容
, 那就需要显卡资源, 此时操作系统就会将此进程的PCB移动到系统描述的显卡结构体的等待队列
中。也就是说,
在操作系统这里, CPU、磁盘、显卡等硬件都存在一个描述其属性的数据结构, 且此数据结构中都存在一个给进程分配资源的队列, 当进程需要某种硬件资源时, 操作系统就会将进程PCB从某队列中移出转移到另一种进程所需资源的队列中
-
硬件分配资源的队列, 此队列遵循队列的先进先出的规则,
进程在此队列中可以看作是在排队, 只有排到的进程才能被硬件赋予资源
-
硬件处理速度是非常快的, 虽然总说磁盘之类的硬件速度很慢, 但也是相对CPU的处理速度说的, 即使是磁盘的处理速度也可能是快到看不出的, 所以
进程状态的变换是非常快的, 是肉眼不可见的, 除非其不变换状态
1. 运行态
只要进程在这个CPU的运行队列中, 就成这个进程在运行态。无论这个进程是在排队, 还是在接受CPU的资源。
2. 进程终止
进程依旧在内存中, 但已经不在接受任何资源, 也不再接受调度, 永远不运行了, 随时可以被释放
进程明明已经不运行了, 为什么不直接将其释放, 而是要存在一个终止态?
释放进程资源也是需要消耗成本、资源的!
假如计算机内存充足, 但此刻操作系统已经非常繁忙了, 难道操作系统要停下现在正在忙的事情, 而转过头来消耗资源释放一个已经不再运行的进程吗?显然是不太合理的3. 进程阻塞
此时此进程想获取CPU资源时需要长时间排队, 操作系统将它放到别的硬件资源队列中了, 发现也需要长时间排队 那么此进程就进入了阻塞状态
当进程此时没有获取CPU资源的同时, 也正在等待非CPU资源, 该进程就进入了阻塞状态
, 此时进程代码就不在运行了, 进程就卡住了此进程PCB不在系统描述CPU结构体的运行队列里
此进程PCB在其他系统描述硬件结构体的等待队列里, 还没有获取此硬件的资源
4. 进程挂起
可能存在一些 进程代码实际上并没有运行的进程 却依旧占用着很大一部分内存空间
, 如果此时内存空间并不充足, 那么操作系统就会考虑将此进程挂起会将进程的程序代码和数据部分 从内存中拿出来, 放到指定的磁盘空间中, 而将此进程的PCB留在内存中
。这样的操作 就称为进程挂起
因为存在进程挂起, 所以当操作系统内存不足的时候, 往往伴随着磁盘被高速访问着
进程挂起会导致磁盘中存在多份的相同程序代码和数据吗?
Linux中实际的进程状态
1. S (sleeping) 睡眠状态
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
using std::cout;
using std::endl;
int main() {
while(1) {
cout << "I am a proess, pid = " << getpid() << endl;
}
return 0;
}
"I am a process, pid = 17590"
S
cout
这个可以在电脑屏幕上输出内容的对象绝大部分时间一定是在等待显卡和显示器资源的
, 显示器的刷新速度与CPU的运算速度根本不是一个量级的。以人的速度, 也是捕捉不到CPU运算这个进程的时刻的, 所以STAT不是 R
进程其实是在等待显卡和显示器的资源, 那其实也就说明进程在等带非CPU的资源, 那也就是指此进程实际上正处于阻塞状态
S (sleeping) 睡眠状态, 其实就是阻塞状态
怎么在Linux观察到R状态呢?
其实很简单, 只需要将上面代码中 cout 语句删除, 让此进程不需要使用其他硬件资源, 那就可以看到此进程在R (running)状态了:
浅度睡眠
。既然有浅度睡眠, 那与之对应的深度睡眠也不会缺少。2. D (disk sleeping) 深度睡眠
特指进程在等待磁盘资源时的阻塞状态
D 状态是不可中断的睡眠状态
, 而 S 状态是可中断的睡眠状态
。S 被称为浅度睡眠
, 而 D 被称为深度睡眠
因为 D 状态的出现, 一般都是在文件传输占用磁盘资源时, 而以现在磁盘的速度传输小的文件也不能观察到 D 状态, 更别说测试能否被中断了。如果是大文件, 博主的垃圾服务器也承受不了太大的文件
一般情况下, D 状态是不容易被看到的
D 是特指进程在等待磁盘资源时的阻塞状态
就可以了为什么要有一个 D 状态?
为什么要单独将 进程等待磁盘资源时的阻塞状态 特定为 D状态?
进程在等待磁盘资源时, 为什么不能中断?
毕竟 D状态 区别于 S状态 的地方就只是 D状态 无法中断将 进程等待磁盘资源时的阻塞状态单独设置成了 D状态, D状态的特点就是无法中断
3. Z (zombie) 僵尸状态
Linux系统中, 当进程不会再被调度、不会再运行时, 不会立马进入 X 终止状态。而是先进入 Z(zombie) 僵尸状态
, 此进程被称为僵尸进程
为什么要进入僵尸状态?
进程在退出之前都会将执行结果、执行进度告知给其父进程或者操作系统
进入 Z状态 表明自己不会再运行了, 但是退出信息还未被父进程或操作系统读取, 不能直接释放
Z状态是维护进程退出信息的状态
task_struct
中部分退出信息
模拟僵尸进程
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
using std::cout;
using std::endl;
int main() {
pid_t id = fork();
if(id == 0) {
cout << "我是子进程, 我的pid是" << getpid() << ", 我运行结束了" << endl;
}
else {
cout << "我是父进程, 我的pid是" << getpid() << ", 我运行没有结束" << endl;
while(1) {}
}
return 0;
}
如果僵尸进程的退出信息一直不被读取不被接收的话, 僵尸进程就一直存在, 进程就会一直在内存中加载, 这其实就是内存泄漏
编写创建子进程的程序时, 要求一定要编写子进程的回收代码
4. T(stopped) 和 t(stracing stop) 暂停状态
t 特指 进程被调试时, 遇到断点时所处的状态
当进程被调试时, 遇到断点整个进程会被暂停, 此时所处的状态就是 t(stracing stop)状态
暂停进程
#include <iostream>
#include <unistd.h>
using std::cout;
using std::endl;
int main() {
while(1) {
cout << "I am a Process, PID = " << getpid() << endl;
}
return 0;
}
kill -19 进程标识符
或 Ctrl+Z
, 将进程暂停:* 孤儿进程
子进程运行没有结束, 但是父进程的运行结束了, 父进程先被回收了, 那么此时的子进程就成了孤儿, 被称为孤儿进程
此时的子进程无法被Ctrl+C终止掉, 只能使用kill -9 PID的方式kill
因为此时的子进程已经成为了后台进程, 在进程状态一栏中, 之前的进程的状态后都会有一个
+
号, 此+
号表示此程序是前台进程而孤儿进程的状态栏中没有
+
号, 表示此进程是一个后台进程
为什么孤儿进程的PPID变成了 1
Linux进程的优先级
那就是在等待获取资源的时候
排队过程的本质其实就是在确定优先级
操作系统中永远都是, 进程是多数的, 而资源是少数的
。如果操作系统不去确定每个进程的优先级是多少, 而是让所有的进程一锅粥的凭本事去竞争资源, 那么一定会造成部分进程"饿死"的情况
。这是操作系统不应该也不允许发生的, 所以进程就需要确定一个优先级, 这样进程在获取资源的时候才不会乱套priority 和 nice
priority
:在Linux系统中,指当前进程的优先级, 此数值越小即表明进程的优先级越高
nice
:在Linux系统中, 可以理解为当前进程优先级被调整的数值
PRI即为priority, NI即为nice
PRI是不能被设置的, 只能通过修改NI值来间接修改PRI值
。即, 如果想要调整进程的优先级, 只能调整NI的数值
或者去修改系统源代码关于修改进程优先级的指令, 可以查看一下man手册中的nice和renice
下面演示使用 top 指令
知道进程的PID时, 进入top, 再按r, 再输入PID, 再输入需要的NI值, 就可以做到NI值的修改(必须为root用户)
PS:不同程序界面的PRI值不同, 可能是因为基准不同
通过top修改进程的NI值, 进而修改了进程的优先级
[-20, 19]
即 Pri(new) = Pri(old) + nice. Pri(old) 指的是今晨最原始的 Pri, 也是 nice 为 0时的值
作者: 哈米d1ch 发表日期:2023 年 3 月 3 日