天天看點

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排程器:程序優先級

繼續閱讀