本文已收录至《Linux知识与编程》专栏!
作者:ARMCSKGT
演示环境:CentOS 7
进程状态及优先级
- 前言
- 正文
- 进程状态
- 就绪运行状态R
- 阻塞
- 睡眠状态 S
- 休眠状态D
- 挂起
- 暂停状态T
- 前台与后台进程
- 待追踪暂停状态t
- 死亡状态 X
- 僵尸状态 Z
- 孤儿进程
- 进程优先级
- 查看进程优先级
- 修改进程优先级
- 最后
前言
操作系统想管理好进程并不简单,对于Linux系统将进程分为不同的状态进行管理;例如运行,阻塞,休眠状态等等,每一种状态都有不同的效果,这样才能发挥他的全部功效,且进程之间也有优先级,本文将为您详细讲解!
正文
进程状态
为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在Linux内核里,进程有时候也叫做任务)。
下面是进程状态在kernel源代码中的定义:/* * The task state array is a strange "bitmap" of * reasons to sleep. Thus "running" is zero, and * you can test for combinations of others with * simple bit tests. */ static const char * const task_state_array[] = { "R (running)", /* 0 */ "S (sleeping)", /* 1 */ "D (disk sleep)", /* 2 */ "T (stopped)", /* 4 */ "t (tracing stop)", /* 8 */ "X (dead)", /* 16 */ "Z (zombie)", /* 32 */ };
分别对应:
- R(就绪运行状态)
- 阻塞状态
– S(睡眠状态/可中断睡眠状态)
– D(休眠状态/不可中断休眠状态)
– 挂起状态- T(暂停状态)
– 前台/后台进程
– t(待追踪暂停状态)- X(死亡状态)
- Z(僵尸状态)
就绪运行状态R
虽然从字面理解,
运行状态就是进程在CPU上运行,但并非如此!
R(运行状态)代表进程在运行队列中排队,而队列的维护是操作系统来做的;可以理解为进程就绪,等待CPU资源的状态!
#include <iostream> using namespace std; int main() { while(true); //死循环 return 0; }
在Linux系统中,新建、就绪、运行都可以看作运行 R 这一个状态,清晰明了!
阻塞
阻塞:进程因为等待某种条件就绪,而导致的一种不推进状态,也就是进程卡住了;阻塞一定是在等待某种资源!
为什么会阻塞?进程要通过等待的方式,等具体的资源被别的进程用完之后,再被自己使用。
简而言之:阻塞就是进程等待某种资源(磁盘,网卡,显卡等各种外设)就绪的过程。
当一个进程运行到某指令时需要外部资源时,操作系统会将进程就会撤出CPU,放到某种资源处进行排队,也就不在CPU上运行了,直到该进程获取到了所有资源(具备运行条件),操作系统会将此进程放入运行队列中进行排队,继续运行!
综上:进程PCB可以被维护在不同的资源队列中; 所以阻塞就是不被调度,一定是因为当前进程需要等待某种资源就绪;在Linux系统下,一定是task_struct结构体需要在某种被操作系统管理的资源下排队!
睡眠状态 S
睡眠状态是可中断休眠,是阻塞的一种!
当进程需要某资源时,操作系统将该进程挂到对应的资源队列排队,此时进程处于可中断休眠状态!#include <iostream> using namespace std; >#include <unistd.h> int main() { while(true) //死循环打印pid { cout<<getpid()<<"进程运行中..."<<endl; sleep(1); } return 0; }
这里可能会有小伙伴疑惑,进程在运行,为什么不是显示R,而是S+?
这里需要说明一下的是,进程向屏幕不断打印的行为也是I/O行为,需要向屏幕这个硬件进行写入,所以每次申请写入屏幕时需要到对应资源队列排队并处于休眠状态,而CPU的运行速度非常快,在此过程中,进行几万次捕捉可能就只有一两次看到进程处于R状态,其他时候都是S+状态。
可能小伙伴还有一个问题,那么S+状态的 + 是什么意思?这个 + 的意思是代表该进程在前台运行,具有体前台后台进程的区别在暂停状态会简单介绍!
处于可中断休眠状态的进程可以被CTRL+C或kill -9等信号终止进程!
休眠状态D
休眠状态是不可中断休眠状态,也是阻塞的一种!
休眠状态下的进程无法别终止,kill信号和操作系统都无法终止,只能等待进程获取到了对应的资源(达到对应条件)才能终止休眠状态; 当然,通过切断电源的方式也可以强制关闭,但是那样操作系统也关闭了,且会损失数据!
这种状态一般可能出现在,例如进程需要在磁盘上I/O,但是此时磁盘I/O阻塞了,此时如果影响进程可能会造成数据损坏,所以操作系统为了保证数据安全,不会插手这件事,一般很少会碰到该状态,如果碰到了该状态,那么计算机很可能就要崩溃了!
所以这种状态的出现往往是为了保护进程,但是最好尽量避免出现这种状态!
挂起
挂起一般又称阻塞挂起,也是一种特殊的阻塞状态!
当系统资源紧张,某些进程在运行时等待资源阻塞时,操作系统保留PCB转而释放该程序的代码和数据(放到硬盘上)腾出空间,此时进程被挂起。
暂停状态T
我们可以使用kill信号的方式让进程进入暂停状态;进入暂停状态的进程不在运行并保持在后台!
使进程暂停的方法:SIGCONT信号
- kill -19 进程PID (暂停进程)
- kill -18 进程PID (恢复进程)
前台与后台进程
在Linux系统下,当进程在前台运行时其状态会附带一个 +,代表该进程是前台进程!
如果是后台进程则没有 + 符号!
进程可以在不同的状态下处于前台和后台运行!
待追踪暂停状态t
该状态是指该进程正在被另一个进程跟踪,例如在Linux下通过GDB调试一个程序,GDB中对被跟踪的进程打一个断点,进程在断点处停下来的时候就处于 t 状态。而在其他时候,被跟踪的进程还是处于其他。对于进程本身来说,T 和 t 状态很类似,都是表示进程暂停下来。
死亡状态 X
当进程结束或被终止时进入死亡状态,这个状态只是一个返回状态,你不会在任务列表里看到这个状态。因为CPU的处理速度非常快,这只是一瞬间!
僵尸状态 Z
僵尸状态是一个与死亡状态相似的状态,通常来说僵尸状态给父进程所准备的!
僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程没有读取到子进程退出的返回代码时就会产生僵死(尸)进程。僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
当父进程创建子进程后,子进程已经运行结束,父进程需要去接收子进程的退出结果(返回值)来判断子进程是否正常运行或运行结果是否正常,然后回收子进程!
所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态。
#include <iostream> using namespace std; #include <unistd.h> #include <cstdlib> int main() { pid_t id = fork(); if(id == 0) { int i = 10; while(i--) { cout<<"子进程运行中..."<<endl; sleep(1); } cout<<"子进程退出!"<<endl; exit(0); } int i = 20; while(i--) { cout<<"父进程运行中..."<<endl; sleep(1); } cout<<"父进程退出!"<<endl; return 0; }
维护僵尸进程的意义是为了让父进程读退出码,但是僵尸进程也有以下等危害:
- 进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态。
- 维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护。
- 那一个父进程创建了很多子进程,就是不回收,就会造成内存资源的浪费;因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间!
- 既然进程一直在内存中保存,就可能存在内存泄漏,需要释放。
所以我们要避免子进程长时间处于僵尸状态,后面在进程控制中会讲解回收监视子进程的办法!
孤儿进程
当父进程先于子进程结束或终止时,子进程就成为孤儿进程!
在Linux系统中,孤儿进程由1号进程bash接管成为后台进程!
为什么bash要接管这个孤儿进程?
- 如果不接管那么子进程在结束后没有父进程回收,一直处于僵尸状态,造成内存泄漏,所以需要bash接管进行释放!
进程优先级
进程除了有不同的状态,还有优先级之分:
- cpu资源分配的先后顺序,就是指进程的优先权(priority)。
- 优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。
- 还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能。
CPU的资源是有限的,合理分配才能最大化效能!
Linux 给我们提供了修改 进程 优先级的权限,目的就是让我们对多任务运行进行合理处理,提高系统运行效率。
查看进程优先级
ps指令可以加上 -al 选项显示进程的其他信息!
ps -al | head -1 && ps -al | grep 进程名(程序名) | grep -v grep
关于PRI与NI值
– PRI是进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小进程的优先级别越高。
– PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为:(新的PRI)=(旧的PRI)+nice
– NI是nice值,其表示进程可被执行的优先级的修正数值。
– 当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行所以,调整进程优先级,在Linux下,就是调整进程nice值。
– nice其取值范围是-20至19,一共40个级别;当我们设置nice值时超过这个区间是无效的!
– 这里需要强调一点的是,进程的nice值不是进程的优先级,他们不是一个概念,但是进程nice值会影响到进程的优先级变化。可以理解nice值是进程优先级的修正修正数据。
– 优先级的修改行为并不是连续的,每次都是在最开始的基础上进行修改(默认为 80)。
– 调度器不允许存在 优先级失衡 的情况,因此优先级修改不能太激进。
修改进程优先级
进程优先级可以修改,但一般我们是不干预操作系统的调度的!
修改步骤:
- top指令打开Linux任务管理器
- 输入 r 进入NI值修改模式
- 输入想修改NI值进程的PID
- 最后输入NI值即可(范围-20~19)
步骤:
动图演示:
最后
进程状态和进程优先级的知识到这里就介绍的差不多了,通过了解进程状态,知道进程的阻塞和和运行的原理;了解进程优先级,学习了修改进程的优先级影响调度机制,这些知识都在为后面进程控制的学习打下基础,后面我们还会介绍有关Linux的环境变量相关知识,敬请期待!
本次 <Linux进程状态及优先级> 就先介绍到这里啦,希望能够尽可能帮助到大家。
如果文章中有瑕疵,还请各位大佬细心点评和留言,我将立即修补错误,谢谢!
🌟其他文章阅读推荐🌟
Linux<进程初识> -CSDN博客
C++ <STL之string的使用> -CSDN博客
C++ <模板> -CSDN博客
C++ <内存管理> -CSDN博客
🌹欢迎读者多多浏览多多支持!🌹