一.进程优先级
优先级概念
由于操作系统具有共享性,操作系统的资源是共享的,当多个进程要访问同一个资源,需要通过一定方式来确定访问顺序,优先级就是进程获取资源的先后顺序。
CPU资源分配的先后顺序,就是指进程的优先权。优先权高的进程有优先执行的权力。配置优先权对多任务环境的Linux很有用,可以改善系统性能。还可以把进程运行到指定CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能。
操作系统中资源是有限的,相对于进程的数量总是少的,那么进程竞争资源是常态,这就引入了优先级来对不同进程进行评判,以此能更有条理地调度进程。
Linux下的进程优先级
优先级是进程的一个属性,那么其也是在进程PCB中,可通过ps -l
来进行查看
这里通过循环输入命令方便查看:
while :; do ps -la |head -1 && ps -la | grep myprocess ;sleep 1;echo ---------------------------------------------- ; done
这里有一个程序,运行后不断打印Hello
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>int main()
{while(1){printf("Hello.\n");sleep(1);}return 0;
}
在shell中输入命令查看,得到以下结果
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
0 S 0 836593 836118 0 80 0 - 694 hrtime pts/0 00:00:00 myprocess
这里重点关注PRI和NI
Linux的进程优先级由PRI和NI
组成。
PRI:优先级(Priority),数字越小优先级越高,默认80,范围[60,99]
NI:nice值,表示进程可被执行的优先级的修正数值,默认0,范围[-20,19]
进程的真正优先级=PRI+NI
,这里的PRI
不能直接进行修改,修改nice
值更为妥当,同时nice
值被覆盖写入。
可通过top
指令修改nice
值:先使用top
命令打开资源管理器,再按r
指令进入进程值修改,最后输入进程PID
后最后输入nice
值。
修改前
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
0 S 0 840588 836118 0 80 0 - 694 hrtime pts/0 00:00:00 myprocess
----------------------------------------------
修改后
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
0 S 0 840588 836118 0 90 10 - 694 hrtime pts/0 00:00:00 myprocess
----------------------------------------------
二.进程调度
调度的概念
CPU调度是对CPU进行分配,即从就绪队列中按照一定的算法(公平、高效的原则)选择一个进程将CPU分配给它运行,以实现进程并发地执行。
一个作业从提交直到完成,往往要经历三级调度
-
进程调度(低级调度)
实现了进程的切换和处理器的切换。使进程在就绪、运行、等待三态之间切换。 -
内存调度(中级调度|平衡负载调度)
主要涉及的资源是内存资源,主要涉及挂起和激活操作。 -
作业调度(高级调度)
高级调度涉及进程创建、就绪、运行、终止这些状态。
Linux的进程切换与调度
进程切换
CPU会维护一个调度队列runqueque
,进程在调度队列中进行排队等待操作系统分配CPU执行。
同时,CPU中会存在大量寄存器,在进程运行过程中会产生大量临时数据放到CPU寄存器中。
当一个进程的时间片结束,需要进行进程切换,这时就要把寄存器中的数据保存在task_struct
中,这些CPU内部的所有临时数据也叫进程硬件上下文。当第二次该进程被调度,就会将曾经保存的硬件上下文进行恢复。
所有的保存是为了最终数据的恢复,所有的恢复都是为了继续上次的运行。
进程调度
Linux进程调度算法是基于时间片的,同时考虑优先级、饥饿和效率
首先下面是调度队列runqueque
的数据结构
其中队列是一个task_struct*
数组维护的,为queue[140]
,一共140个优先级,每个优先级对应一个队列,对应一个数组元素。0-99
为实时优先级,这里不用关心。需要关心的是100-139
这40个优先级,刚好对应nice
的取值范围。
时间片还没有结束的所有进程都按照优先级放在该队列,nr_active
用于记录总共有多少个运行状态的进程。
当要按优先级取出先来的进程时,要在queue[140]
检测每个队列是否有进程,显然从头遍历的复杂度太高。这里加入一个数组:int bitmap[5]
,int
有32位,那么整个数组共有32*5 = 160
个比特位,刚好覆盖140,比特位的位置表示对应优先级的队列,比特位的内容表示该队列是否为空。这样就能实现O(1)
的时间复杂度进行调度。
以上三个成员构成一个结构体
struct q{int nr_active;int bitmap[5];task_struct *queue[140];
};
runqueue
中就有一个struct q* array[2]
的数组,同时维护两个指针active
和expired
分别轮流指向该数组的两个元素。
active
永远指向活动队列,expired
永远指向过期队列,活动队列上的进程会越来越少,过期队列上的进程会越来越多,当活动队列上没有任何进程时,只需要Swap(active, expired)
交换两指针指向的元素空间,过期队列变成活动队列,活动队列变成过期队列,周而复始。