Linux中的进程状态和优先级
作者:格式化、、
本篇文章进行操作系统中进程状态的学习!!!
1、进程状态
1.1、概念
在学习OS中的进程状态时,书上的描述为了在Linux、Windows、Android中都能说的通,往往加深了理解的难度
我们可以先学习具体的Linux进程状态,然后再介绍然后再介绍OS学科的状态如何理解
操作系统是计算机中的一门“哲学”,要考虑很多情况
贴几张不同的进程状态图
1.2、具体的进程状态
1. 具体的进程状态可以分为四种:
运行态:进程只要被加载到运行队列中
如何理解进程被加载到运行队列中?
运行队列也是一个对象,也可以通过描述再组织进行管理
运行队列的属性其中有PCB指针,可以通过队列的性质或复杂的数据结构进行管理
调度器的主要作用是在就绪队列中选择优先级最高的任务运行,如果优先级最高的任务不止一个,则选择队头的任务运行
当运行队列中的进程被调度时,CPU会执行该进程的代码
2. 终止态:进程已经终止,但还存在,只是永远不运行了,随时等待被释放
进程都已经终止了,为什么不立刻释放资源,而要维护一个终止态呢?
因为释放进程的资源需要时间
CPU不可能一直盯着这个进程,可能还会在做其他事情,所以要维护一个终止态
例子:比如你去吃饭,吃完后,叫老板结账,老板可能没回应你,他可能在给别人点餐
3. 阻塞态:进程在等待某种资源时(非CPU资源),资源没有就绪的时候,该进程会到对应的资源等待队列中进行排队,该进程的代码并没有运行,就叫做"阻塞态"
一个进程在被CPU执行的时候,用的不仅仅是CPU的资源
进程可能申请更多的资源,如:磁盘、网卡、显卡、显示器资源和声卡资源等等…
申请对应的资源无法满足时,是需要排队的,比如:CPU资源在运行队列中排队
申请其他慢设备的资源在对应的队列中进行排队
4. 挂起态:当内存不足时,OS会将短期内不会被调度的进程的数据和代码挪动到磁盘中的swap分区,当内存足够时,会重新加载到内存中
1.3、Linux进程状态
Linux内核源代码中的状态
/* * 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 */ };
1.3.1、R运行状态(运行态)
R (running)运行状态:进程是在运行中或在运行队列中
R状态一般是看不出来的,因为CPU的运算速度非常快,当你运行程序时,CPU就已经执行结束
比如在死循环中打印hello world,进程状态一般为"S",因为CPU太快了
CPU一般在等待显示器资源就绪,所以状态一直为S
[lyh_sky@localhost lesson11]$ ls makefile test test.cpp [lyh_sky@localhost lesson11]$ cat test.cpp #include <iostream> #include <unistd.h> #include <sys/types.h> using namespace std; int main() { while (1) { cout << "hello world!!!" << endl; sleep(3); } return 0; } [lyh_sky@localhost lesson11]$ cat makefile test:test.cpp g++ test.cpp -o test .PHONY:clean clean: rm -rf test [lyh_sky@localhost lesson11]$ ./test
通过不断刷新ps指令来查看进程状态的变化:while :; do ps ajx | head -1 && ps ajx | grep ‘test’ | grep -v grep; sleep 2; echo “#############################################”; done
可以通过不断的死循环来让CPU一直不断的工作,因为死循环没有访问外设资源
[lyh_sky@localhost lesson11]$ ls makefile test test.cpp [lyh_sky@localhost lesson11]$ cat test.cpp #include <iostream> #include <unistd.h> #include <sys/types.h> using namespace std; int main() { while (1) { } return 0; } [lyh_sky@localhost lesson11]$ cat makefile test:test.cpp g++ test.cpp -o test .PHONY:clean clean: rm -rf test [lyh_sky@localhost lesson11]$ ./test
1.3.2、S/D睡眠状态(阻塞态)
S (sleeping)睡眠状态:意味着进程在等待事件完成
这里的睡眠也叫做:可中断睡眠(浅睡眠),因为它可以通过指令进行中断
可以使用 kill 【-9】 【进程PID】进行中断
睡眠状态是:该进程申请的资源已经被其他进程使用,该进程在对应的“资源等待队列中进行等待,进程中的代码没有执行
[lyh_sky@localhost lesson11]$ ls makefile test test.cpp [lyh_sky@localhost lesson11]$ cat test.cpp #include <iostream> #include <unistd.h> #include <sys/types.h> using namespace std; int main() { while (1) { cout << "hello world!!!" << endl; sleep(3); } return 0; } [lyh_sky@localhost lesson11]$ cat makefile test:test.cpp g++ test.cpp -o test .PHONY:clean clean: rm -rf test [lyh_sky@localhost lesson11]$ ./test
D磁盘休眠状态(Disk sleep)
D状态也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束
D状态不能通过指令进行中断,必须等它由D状态变成S状态时才能杀死
经过上图可以发现,如果堆磁盘进行读写时,发生中断,会导致严重的问题
所以才有了D状态,D状态就算是OS也不能对其进行释放
D状态不好模拟,但可以使用"dd指令"进行模拟
1.3.3、T/t停止状态
T停止状态(stopped):
可以通过发送 SIGSTOP 信号或快捷键【ctrl+z】给进程来停止(T)进程
这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行
[lyh_sky@localhost lesson11]$ ls makefile test test.cpp [lyh_sky@localhost lesson11]$ cat test.cpp #include <iostream> #include <unistd.h> #include <sys/types.h> using namespace std; int main() { while (1) { cout << getpid() << endl; sleep(2); } return 0; } [lyh_sky@localhost lesson11]$ cat makefile test:test.cpp g++ test.cpp -o test .PHONY:clean clean: rm -rf test [lyh_sky@localhost lesson11]$ ./test
可以通过kill 【-19】 【进程PID】将指定进程发生停止运行信号
kill 【-18】【进程PID】可以恢复重新运行
还有一种t (tracing stop)状态:针对gdb调试的状态
[lyh_sky@localhost lesson11]$ ls makefile test test.cpp [lyh_sky@localhost lesson11]$ cat test.cpp #include <iostream> #include <unistd.h> #include <sys/types.h> using namespace std; int main() { while (1) { cout << getpid() << endl; sleep(2); } return 0; } [lyh_sky@localhost lesson11]$ cat makefile test:test.cpp g++ test.cpp -o test -g .PHONY:clean clean: rm -rf test [lyh_sky@localhost lesson11]$ make g++ test.cpp -o test -g [lyh_sky@localhost lesson11]$ gdb test
1.3.3、X死亡状态
X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态
1.3.4、Z(zombie)僵尸进程
概念:
注意:僵尸进程是不能被杀死的,因为已经是僵尸了,不能再死第二次!!!
当一个进程退出的时候,一般不会直接进入X状态(死亡,资源可以立马被会回收),而是进入Z状态
僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用)没有读取到子进程退出的返回代码时就会产生僵死(尸)进程
僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码
只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态
进程被创建出来是因为用户有任务要交给它执行,当进程退出的时候,我们不知道这个进程完成的怎么样了,一般需要将进程的执行结果告知给父进程和OS
模拟Z状态:创建子进程,子进程退出,父进程不退出(还在运行),子进程退出之后的状态就是Z
[lyh_sky@localhost lesson11]$ cat test.cpp #include <iostream> #include <cstdlib> #include <unistd.h> #include <sys/types.h> using namespace std; int main() { pid_t id = fork(); int cnt = 3; if (id == 0) { while (cnt) { cout << "我是子进程,我还有" << cnt-- << "秒时间结束" << endl; sleep(1); } cout << "进入僵尸状态" << endl; // 结束子进程,并且返回0 exit(0); } else { while (1) {} } return 0; } [lyh_sky@localhost lesson11]$ cat makefile test:test.cpp g++ test.cpp -o test -g .PHONY:clean clean: rm -rf test [lyh_sky@localhost lesson11]$ ./test
僵尸进程危害:
如果没有人回收子进程的僵尸,该状态会一直被维!该进程的相关资源(task_struct)不会被释放
那一个父进程创建了很多子进程,就是不回收,就会造成内存资源的浪费!因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量,是要在内存的某个位置进行开辟空间!
这样就会造成内存泄漏!!!
2、孤儿进程
init进程:它是内核启动的第一个用户级进程
父进程如果提前退出,那么子进程后退出,进入Z之后,那该如何处理呢?
父进程先退出,子进程就称之为“孤儿进程”
孤儿进程被1号init进程领养,当然要有init进程回收喽
[lyh_sky@localhost lesson11]$ cat test.cpp #include <iostream> #include <cstdlib> #include <unistd.h> #include <sys/types.h> using namespace std; int main() { pid_t id = fork(); int cnt = 3; if (id != 0) { while (cnt) { cout << "我是父进程,我还有" << cnt-- << "秒时间结束" << endl; sleep(1); } cout << "父进程退出" << endl; exit(0); } else { while (1) {} } return 0; } [lyh_sky@localhost lesson11]$ cat makefile test:test.cpp g++ test.cpp -o test -g .PHONY:clean clean: rm -rf test [lyh_sky@localhost lesson11]$ ./test
3、Linux进程优先级
3.1、优先级概念
进程优先级和权限的区别?
进程优先级是:进程获取外设资源的先后顺序问题
权限是:拥有者、所属组和other是否可以进行读写执行操作
它们的区别是:在学校中,我们去吃饭,食堂分教师和学生,我们不能去教师食堂打饭,这就是权限。我们打饭时,需要排队,排在前面可以先打到饭,后面的也一样可以打到饭,这就是优先级
概念:
cpu资源分配的先后顺序,就是指进程的优先权(priority)
优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能
还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能
为什么要存在进程优先级呢???
排队的本质就是确认进程的优先级
内存里面永远都是进程占大多数,而资源是少数(外设:磁盘、网卡、显卡…)
进程竞争资源是常态,OS要确认进程的先后循序,不然就乱套了
3.2、Linux下进程优先级的操作
使用【ps -al】指令查看进程更多的属性(包含进程优先级):
top指令
进入top后按“r”–>输入进程PID–>输入nice值
[lyh_sky@localhost lesson11]$ ls makefile test test.cpp [lyh_sky@localhost lesson11]$ cat test.cpp #include <iostream> #include <cstdlib> #include <unistd.h> #include <sys/types.h> using namespace std; int main() { while (1) { cout << "hello world!!!" << endl; sleep(2); } return 0; } [lyh_sky@localhost lesson11]$ cat makefile test:test.cpp g++ test.cpp -o test -g .PHONY:clean clean: rm -rf test [lyh_sky@localhost lesson11]$ ./test
我们很容易注意到其中的几个重要信息,有下:
- UID : 代表执行者的身份
- PID : 代表这个进程的代号
- PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
- PRI :代表这个进程可被执行的优先级,其值越小越早被执行
- NI :代表这个进程的nice值
3.3、PRI 和 NI
PRI也还是比较好理解的,即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小进程的优先级别越高
NI就是nice值,其表示进程可被执行的优先级的修正数值
PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为: PRI(new) = PRI(old) + nice
当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行
调整进程优先级,在Linux下,就是调整进程nice值
nice其取值范围是-20至19,一共40个级别,调整NI时,会重新变成初始值80,然后+NI值
例如PRI:70 NI:-10,把NI更改成10时,PRI = 80 + 10 = 90
在root用户下,使用top指令修改进程优先级:
[lyh_sky@localhost lesson11]$ ls makefile test test.cpp [lyh_sky@localhost lesson11]$ cat test.cpp #include <iostream> #include <cstdlib> #include <unistd.h> #include <sys/types.h> using namespace std; int main() { while (1) { cout << "hello world!!!" << endl; cout << "我的pid是: " << getpid() << endl; sleep(2); } return 0; } [lyh_sky@localhost lesson11]$ cat makefile test:test.cpp g++ test.cpp -o test -g .PHONY:clean clean: rm -rf test [lyh_sky@localhost lesson11]$ ./test
进入top指令后输入r,然后回车,然后输入需要修改进程优先级的pid
输入pid后回车,接着输入NI值,就能修改进程优先级了
3.4、PRI vs NI
需要强调一点的是,进程的nice值不是进程的优先级,他们不是一个概念,但是进程nice值会影响到进程的优先级变化
可以理解nice值是进程优先级的修正修正数据
4、其他概念
1. 竞争性:系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级
2. 独立性:多进程运行,需要独享各种资源,多进程运行期间互不干扰
比如:我们在下载东西时,同时打开浏览器进行浏览,当浏览器挂掉了,是不会影响下载的,反之下载挂掉了,也不会影响浏览器
进程具有独立性,不会因为一个进程挂断或者出现异常,而导致其他进程出现问题
3. 并行:多个进程在多个CPU下,分别同时进行运行,这称之为并行
4. 并发:多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发
操作系统,就是简单的根据队列来进行先后调度的吗?有没有可能突然来了一个优先级更高的进程?
操作系统中有二种内核,分为“抢占式内核”和“非抢占式内核”
当正在运行的低优先级进程,如果来了一个优先级更高的进程,调度器会直接把正在执行的进程从CPU上剥离,放上优先级更高的进程,这就是“进程抢占”(抢占式内核)
5. 进程上下文:进程在运行中产生的各种寄存器数据,就叫做进程的硬件上下文数据
当进程被剥离:需要保存上下文数据
当进程恢复的时候:需要将曾经保存的 上下文数据恢复到寄存器中
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。