天天看点

LINUX 进程优先级与调度策略

1. 调度策略

//https://android.googlesource.com/kernel/common/+/refs/heads/android-4.14/include/uapi/linux/sched.h
.......
/*
 * Scheduling policies
 */
#define SCHED_NORMAL		0
#define SCHED_FIFO		1
#define SCHED_RR		2
#define SCHED_BATCH		3
/* SCHED_ISO: reserved but not implemented yet */
#define SCHED_IDLE		5
#define SCHED_DEADLINE		6
/*    
0: 默认的调度策略,针对的是普通进程。
1:针对实时进程的先进先出调度。适合对时间性要求比较高但每次运行时间比较短的进程。
2:针对的是实时进程的时间片轮转调度。适合每次运行时间比较长得进程。
3:针对批处理进程的调度,适合那些非交互性且对cpu使用密集的进程。
SCHED_ISO:是内核的一个预留字段,目前还没有使用
5:适用于SCHED_DEADLINE优先级较低的后台进程。
6:SCHED_DEADLINED在规定的期限内cpu必须要花指定的时间在本进程上。

SCHED_NORMAL和SCHED_BATCH调度普通的非实时进程,
SCHED_FIFO和SCHED_RR和SCHED_DEADLINE则采用不同的调度策略调度实时进程,
SCHED_IDLE则在系统空闲时调用idle进程.
*/
           

可以通过chrt查看进程的调度策略和优先级

pencil: $ chrt -p 551
pid 551's current scheduling policy: SCHED_BATCH
pid 551's current scheduling priority: 0  #实时优先级,普通进程为0

pencil: $ chrt -p 511
pid 511's current scheduling policy: SCHED_FIFO
pid 511's current scheduling priority: 15 #实时进程优先级 1(优先级最低)~99(优先级最高)
           

2. 内核优先级表示

2.1 概述
  • 内核使用0~139表示内部优先级,

    task_struct.prio

    的数值越低, 优先级越高(注意:这里指的是

    task_struct.prio

    ,还有其他优先级表示,注意区分)
  • 实时优先级范围是 0 - MAX_RT_PRIO-1(即0 ~ 99)
  • 普通进程的静态优先级范围是从 MAX_RT_PRIO - MAX_PRIO-1(即100~139),nice的值[-20,19]的映射的范围就是对应优先级的100 - 139,
优先级范围 描述
0——99 实时进程
100——139 非实时进程

贴下这部分的代码,内核里面有详尽的注释

https://elixir.bootlin.com/linux/v5.1.5/source/include/linux/sched/prio.h

#define MAX_NICE	19
#define MIN_NICE	-20
#define NICE_WIDTH	(MAX_NICE - MIN_NICE + 1)

/*
 * Priority of a process goes from 0..MAX_PRIO-1, valid RT
 * priority is 0..MAX_RT_PRIO-1, and SCHED_NORMAL/SCHED_BATCH
 * tasks are in the range MAX_RT_PRIO..MAX_PRIO-1. Priority
 * values are inverted: lower p->prio value means higher priority.
 
 * 这部分能看到SCHED_NORMAL/SCHED_BATCH的优先级在MAX_RT_PRIO..MAX_PRIO-1(100 ~ 139),
 * 即普通非实时进程。而p->prio越小优先级越高
 
 * The MAX_USER_RT_PRIO value allows the actual maximum
 * RT priority to be separate from the value exported to
 * user-space.  This allows kernel threads to set their
 * priority to a value higher than any user task. Note:
 * MAX_RT_PRIO must not be smaller than MAX_USER_RT_PRIO.
 */

#define MAX_USER_RT_PRIO	100
#define MAX_RT_PRIO		MAX_USER_RT_PRIO

#define MAX_PRIO		(MAX_RT_PRIO + NICE_WIDTH)
#define DEFAULT_PRIO		(MAX_RT_PRIO + NICE_WIDTH / 2)

/*
 * Convert user-nice values [ -20 ... 0 ... 19 ]
 * to static priority [ MAX_RT_PRIO..MAX_PRIO-1 ],
 * and back.
 看到NICE值对应的是MAX_RT_PRIO..MAX_PRIO-1
  [ -20 ... 0 ... 19 ] ~ [ MAX_RT_PRIO..MAX_PRIO-1 ]
 */

#define NICE_TO_PRIO(nice)	((nice) + DEFAULT_PRIO)
#define PRIO_TO_NICE(prio)	((prio) - DEFAULT_PRIO)
           
2.2 内核优先级表示

在内核task_struct中有四个字段表示优先级,分别是:static_prio,rt_priority,normal_prio,prio,这四种优先级需要注意 rt_priority 不是越小优先级越高,而是越大优先级越高,见下文

静态优先级 实时优先级 普通优先级 动态优先级
static_prio rt_priority normal_prio prio
https://elixir.bootlin.com/linux/v5.1.5/source/include/linux/sched.h#L585
struct task_struct {
............
	int				recent_used_cpu;
	int				wake_cpu;
#endif
	int				on_rq;
	 /* prio: 动态优先级, 范围为100~139,与静态优先级和补偿(bonus)有关*/
	int				prio;
    /* static_prio: 静态优先级
     * static_prio = NICE_TO_PRIO(nice) = (nice) + DEFAULT_PRIO = (nice) + (MAX_RT_PRIO + NICE_WIDTH / 2)
     * 即:static_prio = 100 + nice + 20 
     * nice值为-20~19,所以static_prio值为100~139
     */
	int				static_prio;
    /* normal_prio: 动态优先级 */
	int				normal_prio;
    /* 实时进程优先级 1 ~ 99*/
	unsigned int			rt_priority;
	const struct sched_class	*sched_class;
............
           
  • prio

    动态优先级

    (1)值越小,进程优先级越高

    (2)prio值为 -1 ~139

    (3)调度器调度采用该优先级调度。该优先级会根据是否为实时进程采用下面的三个优先级来计算

  • static_prio

    静态优先级

    (1)值越小,进程优先级越高

    (2)static_prio值为100~139,static_prio = nice + MAX_RT_PRIO + NICE_WIDTH / 2

    (3)缺省值是 120

    (4)用户空间可以通过nice()或者setpriority对该值进行修改。通过getpriority可以获取该值。

    (5)新创建的进程会继承父进程的static priority。

  • normal_prio

    动态优先级,

    (1)值越小,进程优先级越高

    (2)normal_prio值为 -1 ~139

    (3)表示基于进程的静态优先级static_prio和调度策略计算出的优先级。普通进程static_prio = normal_prio

    (4)进程fork时, 子进程会继承父进程的普通优先级

    下面代码即为normal_prio计算方式

    https://elixir.bootlin.com/linux/v5.1.5/source/kernel/sched/core.c#L820
    
    /*
     * __normal_prio - return the priority that is based on the static prio
     */
    static inline int __normal_prio(struct task_struct *p)
    {
    	return p->static_prio;
    }
    
    /*
     * Calculate the expected normal priority: i.e. priority
     * without taking RT-inheritance into account. Might be
     * boosted by interactivity modifiers. Changes upon fork,
     * setprio syscalls, and whenever the interactivity
     * estimator recalculates.
     */
    static inline int normal_prio(struct task_struct *p)
    {
    	int prio;
    
    	if (task_has_dl_policy(p))
    		prio = MAX_DL_PRIO-1;
    	else if (task_has_rt_policy(p))
    		prio = MAX_RT_PRIO-1 - p->rt_priority;
    	else
    		prio = __normal_prio(p);
    	return prio;
    }
               
  • rt_priority

    实时优先级

    (1)用于表示实时进程优先级

    (2)值越大,进程优先级越高

    (3)rt_priority值为 0~99,0是普通进程,1~99是实时进程,99的优先级最高

  • 为什么会有-1这个优先级?

    -1 是 DEADLINE类进程来说固定值,采用EDF调度策略,即对应SCHED_DEADLINE这个策略。比RT进程和normal进程的优先级还要高,在规定的期限内,cpu必须要花指定的时间在本进程上。

  • 为什么表示动态优先级需要两个值prio和normal_prio?

    调度器会考虑的优先级则保存在prio. 由于在某些情况下内核需要暂时提高进程的优先级, 因此需要用prio表示. 由于这些改变不是持久的, 因此静态优先级static_prio和普通优先级normal_prio不受影响.

  • rt_priority数值越大, 实时进程优先级越高, rt_priority 与 prio 如何对应?

    在内核代码里有时候会这样调整一个进程的优先级,这里sched_priority优先级指的是rt_priority,越大优先级越高

    struct sched_param param = { .sched_priority = 2 };
    	rv = sched_setscheduler(current, SCHED_RR, &param);
               
    其转化代码在这块

    prio = MAX_RT_PRIO-1 - p->rt_priority;

    即:prio = 99 - rt_priority
    static inline int normal_prio(struct task_struct *p)
    {
    	int prio;
    
    	if (task_has_dl_policy(p))
    		prio = MAX_DL_PRIO-1;
    	else if (task_has_rt_policy(p))
    		prio = MAX_RT_PRIO-1 - p->rt_priority; // 99 - rt_priority
    	else
    		prio = __normal_prio(p);
    	return prio;
    }
               
2.3 各个命令看到优先级

在用户空间,进程优先级有两种含义:nice value和scheduling priority。对于普通进程而言,进程优先级就是nice value,从-20(优先级最高)~19(优先级最低),通过修改nice value可以改变普通进程获取cpu资源的比例。随着实时需求的提出,进程又被赋予了另外一种属性scheduling priority,而这些进程被称为实时进程。实时进程的优先级的范围可以通过sched_get_priority_min和sched_get_priority_max,对于linux而言,实时进程的scheduling priority的范围是1(优先级最低)~99(优先级最高)。当然,普通进程也有scheduling priority,被设定为0。

  • ps

    ps看到的优先级是越大优先级越高

  • systrace

    越小优先级越高

  • top

    小于99会标为RT,[100~149] --> [-20 ~ 19]

  • ps -e -o pri,rtprio,cmd

用户空间优先级

在用户空间,进程优先级有两种含义:nice value和scheduling priority。对于普通进程而言,进程优先级就是nice value,从-20(优先级最高)~19(优先级最低),通过修改nice value可以改变普通进程获取cpu资源的比例。随着实时需求的提出,进程又被赋予了另外一种属性scheduling priority,而这些进程被称为实时进程。实时进程的优先级的范围可以通过sched_get_priority_min和sched_get_priority_max,对于linux而言,实时进程的scheduling priority的范围是1(优先级最低)~99(优先级最高)。当然,普通进程也有scheduling priority,被设定为0。

LINUX 进程优先级与调度策略
  • 进程优先级查看

    systrace 中看到的优先级是越小越高,

    在ps -lA看到的优先级是越大优先级越高,ps看到0 - 39对应内核优先级中的100 - 139,而40 - 139 对应内核优先级中的RT优先级0 - 99,实时优先级是越大优先级越高

    在内核设置实时调度策略 0~99 越大优先级越高

参考:

Linux进程优先级的处理–Linux进程的管理与调度(二十二)

Linux进程和线程的调度与优先级

Linux调度器:进程优先级

继续阅读