本文主要概述Linux下进程与线程的调度算法和优先级相关内容。
为便于快速了解和复习,本节先给出结论性的大致概述,后面再详细介绍相关内容。关于进程优先级和调度策略的大致信息如下。
命令行/shell脚本中,可以通过
ps
,
top
,
chrt
来查看和修改调度和优先级属性。
代码中,通过系统调用
sched_setscheduler()
设置进程调度和优先级属性(需要包含
sched.h
)。
代码中,通过库函数
pthread_attr_setschedpolicy()
设置线程调度和优先级属性(需要包含
pthread.h
)。
一般子进程会继承父进程的调度策略,在Linux 2.6.32之后,可以使用
SCHED_RESET_ON_FORK
按位与参数的方式调用
sched_setscheduler()
, 使用之后效果是:如果调用进程使用
SCHED_FIFO
或
SCHED_RR
调度策略,使用
SCHED_RESET_ON_FORK
后fork创建的子进程创建时将会自动重置为
SCHED_OTHER
调度策略;如果调用进程使用负值nice,那么使用
SCHED_RESET_ON_FORK
后fork创建的子进程创建时将会自动将其nice重置为0。 这个标记激活时只有当进程具有
CAP_SYS_NICE
标记时才能被重置,而这个
CAP_SYS_NICE
标记在使用fork()创建子进程后,在子进程中被禁止。
进程优先级概念
进程优先级值越小,优先级越大。
linux下的进程调度优先级是从
-20
到
19
,一共
40
个级别,数字越大,表示进程的优先级越低。默认时候,进程的优先级是0。
如果不是
root
权限,则侄只能降调度优先级而不能提高,即使是自己用户的进程,自己把它调高了后,优先级也不能再被调会原来的值了,除非使用
root
用户来调回去。
系统重启后,对进程优先级的调整全部失效,所有进程的调度回到默认的初始级别。
参考: Linux进程优先级的调整方法
进程优先级是
PRI
值不是
Nice
值,但是
Nice
值会影响优先级
PRI
也还是比较好理解的,即进程的优先级,或者通俗点说就是程序被
CPU
执行的先后顺序,此值越小进程的优先级别越高。那
NI
呢?就是我们所要说的
nice
值了,其表示进程可被执行的优先级的修正数值。如前面所说,
PRI
值越小越快被执行,那么加入
nice
值后,将会使得
PRI
变为:
PRI(new)=PRI(old)+nice
。
到目前为止,更需要强调一点的是,进程的
nice
值不是进程的优先级,他们不是一个概念,但是进程
nice
值会影响到进程的优先级变化。
通过调整
nice
值,更改进程优先级,
nice
调整范围是
-20~19
。
进程优先级与变动影响因素与原理
调度策略:
RR
调度和
FIFO
调度的进程属于实时进程,以分时调度(
OTHER
)的进程是非实时进程。
FIFO
(先进先出)和
RR
(时间片轮转)用于实时进程,
OTHER
(分时调度)用于非实时进程;实时进程会抢占普通进程;
FIFO
会导致同优先级实时进程始终占用
CPU
,
RR
会保证同优先级实时进程按时间片轮流执行。
参考: SCHED_OTHER,SCHED_FIFO,SCHED_RR
实时进程优先级高于普通进程,实时进程使用静态优先级调度、非实时进程使用动态优先级调度,非实时进程动态优先级通过
nice
调整、可能受
bonus
影响。
Linux进程有两种优先级:普通进程优先级(使用
SCHED_NORMAL
调度策略),以及实时进程优先级(使用
SCHED_FIFO
或
SCHED_RR
调度策略)
不同调度策略的实时进程只有在相同优先级时才有可比性,任何时候,实时进程的优先级都高于普通进程
Linux对实时进程使用静态优先级调度,对普通的进程(非实时进程),根据动态优先级进行调度。
实时进程,只有静态优先级(在
0~MAX_RT_PRIO-1
间,默认
MAX_RT_PRIO
为
100
), 内核不会再根据休眠等因素对其静态优先级做调整;
实时进程
0-99
号优先级每一个优先级对应一个优先级队列(链表),先执行数值高的对应的链表(0号最低),可由后面所述的
chrt
以及 相应函数修改该优先级;
非实时进程静态优先级可通过
nice
值(
-20~19
)调整:
static_prio=MAX_RT_PRIO + nice + 20
。
nice
只影响非实时进程(静态优先级在
100~139
之间),
nice
越大静态优先级值越大,优先级越低。
非实时进程动态优先级根据静态优先级和
bonus
计算:
dynamic_prio = max (100, min (static_prio - bonus + 5, 139))
bonus
反映进程平均睡眠时间(范围
0~10
),睡眠时间越多越可能是交互进程,每次轮到它运行时它就越可能不会使用完时间片再释放
cpu
。
参考: Linux进程调度原理 以及 给进程设置实时优先级
查看与设置优先级与进程线程信息等的方法
通过
ps
查看进程信息
ps -el
可查看进程优先级和nice值,命令执行结果:NI列显示的每个进程的nice值(主要针对非实时进程),PRI是进程的优先级(如果是实时进程就是静态优先级,如果是非实时进程,就是动态优先级)。
参考: Linux进程调度原理
通过
top
查看进程信息
其中的优先级和ps的pri不一样,ps中一般为0的进程,在top中为20,这个优先级应该对应于前面所说的实时进程优先级,实时进程只使用1-99号优先级队列,序号越大优先级越高,0号留给普通进程使用。
chrt -p <pid>
查看进程属性
通过
chrt
查看和修改进程调度和优先级属性,它一般也是调用后面的
sched_setscheduler()
实现的。
注意:
char -o pri command
设置为SCHED_OTHER调度方式优先级别值只能是0表示比任何实时优先级都低,非实时进程不使用该值决定调度行为,普通进程使用自己的调度策略。
参考:
man chrt
通过
sched_setscheduler()
设置进程调度和优先级属性
效果类似
chrt
,
关于priority的注意:这里参数中的pri不是ps的nice也不是priority,而是代码中的sched_priority,用于表示一系列可执行进程列表。实时进程中该值范围为1-99越大表示优先级越高, 非实时只能是0表示始终比实时进程低,并且非实时进程的优先级不由此值确定而是由其内部动态优先级确定。chrt中的pri应该也类似含义,例如:
chrt -f -p 10 pid
则ps中pri为-11,
chrt -f -p 1 pid
则ps中pri为-2)为0,进程命令为command,其它可以根据选项参数设置。
关于prioriti的注意:一般子进程会继承父进程的调度策略,在Linux 2.6.32之后,可以使用
SCHED_RESET_ON_FORK
按位与参数的方式调用
sched_setscheduler()
, 使用之后效果是:如果调用进程使用
SCHED_FIFO
或
SCHED_RR
调度策略,使用
SCHED_RESET_ON_FORK
后fork创建的子进程创建时将会自动重置为
SCHED_OTHER
调度策略;如果调用进程使用负值nice,那么使用
SCHED_RESET_ON_FORK
后fork创建的子进程创建时将会自动将其nice重置为0。 这个标记激活时只有当进程具有
CAP_SYS_NICE
标记时才能被重置,而这个
CAP_SYS_NICE
标记在使用fork()创建子进程后,在子进程中被禁止。
参考:
man sched_setscheduler
和
线程优先级设置
通过
pthread_attr_setschedpolicy()
设置线程调度和优先级属性
效果类似
sched_setscheduler()
, 但是用于线程而非进程。
参考:
man pthread_attr_setschedpolicy