本文為原創,轉載請注明:http://www.cnblogs.com/tolimit/
引言
調度器介紹
- 公平:保證每個進程得到合理的CPU時間。
- 高效:使CPU保持忙碌狀態,即總是有進程在CPU上運行。
- 響應時間:使交互用戶的響應時間盡可能短。
- 周轉時間:使批處理用戶等待輸出的時間盡可能短。
- 吞吐量:使單位時間內處理的進程數量盡可能多。
- 負載均衡:在多核多處理器系統中提供更高的性能
進程
- 實時進程:對系統的響應時間要求很高,它們需要短的響應時間,並且這個時間的變化非常小,典型的實時進程有音樂播放器,視頻播放器等。
- 普通進程:包括交互進程和非交互進程,交互進程如文本編輯器,它會不斷的休眠,又不斷地通過鼠標鍵盤進行喚醒,而非交互進程就如后台維護進程,他們對IO,響應時間沒有很高的要求,比如編譯器。
調度策略
- SCHED_NORMAL:普通進程使用的調度策略,現在此調度策略使用的是CFS調度器。
- SCHED_FIFO:實時進程使用的調度策略,此調度策略的進程一旦使用CPU則一直運行,直到有比其更高優先級的實時進程進入隊列,或者其自動放棄CPU,適用於時間性要求比較高,但每次運行時間比較短的進程。
- SCHED_RR:實時進程使用的時間片輪轉法策略,實時進程的時間片用完后,調度器將其放到隊列末尾,這樣每個實時進程都可以執行一段時間。適用於每次運行時間比較長的實時進程。
調度
- 調用cond_resched()時
- 顯式調用schedule()時
- 從系統調用或者異常中斷返回用戶空間時
- 從中斷上下文返回用戶空間時
- 在系統調用或者異常中斷上下文中調用preempt_enable()時(多次調用preempt_enable()時,系統只會在最后一次調用時會調度)
- 在中斷上下文中,從中斷處理函數返回到可搶占的上下文時(這里是中斷下半部,中斷上半部實際上會關中斷,而新的中斷只會被登記,由於上半部處理很快,上半部處理完成后才會執行新的中斷信號,這樣就形成了中斷可重入, 但是即使是中斷下半部, 也是不能夠被調度的)
數據結構
組成形式
圖1
組調度(struct task_group)
linux可以以以下兩種方式進行進程的分組:
- 用戶ID:按照進程的USER ID進行分組,在對應的/sys/kernel/uid/目錄下會生成一個cpu.share的文件,可以通過配置該文件來配置用戶所占CPU時間比例。
- cgourp(control group):生成組用於限制其所有進程,比如我生成一個組(生成后此組為空,里面沒有進程),設置其CPU使用率為10%,並把一個進程丟進這個組中,那么這個進程最多只能使用CPU的10%,如果我們將多個進程丟進這個組,這個組的所有進程平分這個10%。
1 /* 進程組,用於實現組調度 */ 2 struct task_group { 3 /* 用於進程找到其所屬進程組結構 */ 4 struct cgroup_subsys_state css; 5 6 #ifdef CONFIG_FAIR_GROUP_SCHED 7 /* CFS調度器的進程組變量,在 alloc_fair_sched_group() 中進程初始化及分配內存 */ 8 /* 該進程組在每個CPU上都有對應的一個調度實體,因為有可能此進程組同時在兩個CPU上運行(它的A進程在CPU0上運行,B進程在CPU1上運行) */ 9 struct sched_entity **se; 10 /* 進程組在每個CPU上都有一個CFS運行隊列(為什么需要,稍后解釋) */ 11 struct cfs_rq **cfs_rq; 12 /* 用於保存優先級默認為NICE 0的優先級 */ 13 unsigned long shares; 14 15 #ifdef CONFIG_SMP 16 atomic_long_t load_avg; 17 atomic_t runnable_avg; 18 #endif 19 #endif 20 21 #ifdef CONFIG_RT_GROUP_SCHED 22 /* 實時進程調度器的進程組變量,同 CFS */ 23 struct sched_rt_entity **rt_se; 24 struct rt_rq **rt_rq; 25 26 struct rt_bandwidth rt_bandwidth; 27 #endif 28 29 struct rcu_head rcu; 30 /* 用於建立進程鏈表(屬於此調度組的進程鏈表) */ 31 struct list_head list; 32 33 /* 指向其上層的進程組,每一層的進程組都是它上一層進程組的運行隊列的一個調度實體,在同一層中,進程組和進程被同等對待 */ 34 struct task_group *parent; 35 /* 進程組的兄弟結點鏈表 */ 36 struct list_head siblings; 37 /* 進程組的兒子結點鏈表 */ 38 struct list_head children; 39 40 #ifdef CONFIG_SCHED_AUTOGROUP 41 struct autogroup *autogroup; 42 #endif 43 44 struct cfs_bandwidth cfs_bandwidth; 45 };
調度實體(struct sched_entity)
1 /* 一個調度實體(紅黑樹的一個結點),其包含一組或一個指定的進程,包含一個自己的運行隊列,一個父親指針,一個指向需要調度的運行隊列指針 */ 2 struct sched_entity { 3 /* 權重,在數組prio_to_weight[]包含優先級轉權重的數值 */ 4 struct load_weight load; /* for load-balancing */ 5 /* 實體在紅黑樹對應的結點信息 */ 6 struct rb_node run_node; 7 /* 實體所在的進程組 */ 8 struct list_head group_node; 9 /* 實體是否處於紅黑樹運行隊列中 */ 10 unsigned int on_rq; 11 12 /* 開始運行時間 */ 13 u64 exec_start; 14 /* 總運行時間 */ 15 u64 sum_exec_runtime; 16 /* 虛擬運行時間,在時間中斷或者任務狀態發生改變時會更新 17 * 其會不停增長,增長速度與load權重成反比,load越高,增長速度越慢,就越可能處於紅黑樹最左邊被調度 18 * 每次時鍾中斷都會修改其值 19 * 具體見calc_delta_fair()函數 20 */ 21 u64 vruntime; 22 /* 進程在切換進CPU時的sum_exec_runtime值 */ 23 u64 prev_sum_exec_runtime; 24 25 /* 此調度實體中進程移到其他CPU組的數量 */ 26 u64 nr_migrations; 27 28 #ifdef CONFIG_SCHEDSTATS 29 /* 用於統計一些數據 */ 30 struct sched_statistics statistics; 31 #endif 32 33 #ifdef CONFIG_FAIR_GROUP_SCHED 34 /* 代表此進程組的深度,每個進程組都比其parent調度組深度大1 */ 35 int depth; 36 /* 父親調度實體指針,如果是進程則指向其運行隊列的調度實體,如果是進程組則指向其上一個進程組的調度實體 37 * 在 set_task_rq 函數中設置 38 */ 39 struct sched_entity *parent; 40 /* 實體所處紅黑樹運行隊列 */ 41 struct cfs_rq *cfs_rq; 42 /* 實體的紅黑樹運行隊列,如果為NULL表明其是一個進程,若非NULL表明其是調度組 */ 43 struct cfs_rq *my_q; 44 #endif 45 46 #ifdef CONFIG_SMP 47 /* Per-entity load-tracking */ 48 struct sched_avg avg; 49 #endif 50 };
實際上,紅黑樹是根據 struct rb_node 建立起關系的,不過 struct rb_node 與 struct sched_entity 是一一對應關系,也可以簡單看為一個紅黑樹結點就是一個調度實體。可以看出,在 struct sched_entity 結構中,包含了一個進程(或進程組)調度的全部數據,其被包含在 struct task_struct 結構中的se中,如下:
1 struct task_struct { 2 ........ 3 /* 表示是否在運行隊列 */ 4 int on_rq; 5 6 /* 進程優先級 7 * prio: 動態優先級,范圍為100~139,與靜態優先級和補償(bonus)有關 8 * static_prio: 靜態優先級,static_prio = 100 + nice + 20 (nice值為-20~19,所以static_prio值為100~139) 9 * normal_prio: 沒有受優先級繼承影響的常規優先級,具體見normal_prio函數,跟屬於什么類型的進程有關 10 */ 11 int prio, static_prio, normal_prio; 12 /* 實時進程優先級 */ 13 unsigned int rt_priority; 14 /* 調度類,調度處理函數類 */ 15 const struct sched_class *sched_class; 16 /* 調度實體(紅黑樹的一個結點) */ 17 struct sched_entity se; 18 /* 調度實體(實時調度使用) */ 19 struct sched_rt_entity rt; 20 #ifdef CONFIG_CGROUP_SCHED 21 /* 指向其所在進程組 */ 22 struct task_group *sched_task_group; 23 #endif 24 ........ 25 }
在 struct sched_entity 結構中,值得我們注意的成員是:
- load:權重,通過優先級轉換而成,是vruntime計算的關鍵。
- on_rq:表明是否處於CFS紅黑樹運行隊列中,需要明確一個觀點就是,CFS運行隊列里面包含有一個紅黑樹,但這個紅黑樹並不是CFS運行隊列的全部,因為紅黑樹僅僅是用於選擇出下一個調度程序的算法。很簡單的一個例子,普通程序運行時,其並不在紅黑樹中,但是還是處於CFS運行隊列中,其on_rq為真。只有准備退出、即將睡眠等待和轉為實時進程的進程其CFS運行隊列的on_rq為假。
- vruntime:虛擬運行時間,調度的關鍵,其計算公式:一次調度間隔的虛擬運行時間 = 實際運行時間 * (NICE_0_LOAD / 權重)。可以看出跟實際運行時間和權重有關,紅黑樹就是以此作為排序的標准,優先級越高的進程在運行時其vruntime增長的越慢,其可運行時間相對就長,而且也越有可能處於紅黑樹的最左結點,調度器每次都選擇最左邊的結點為下一個調度進程。注意其值為單調遞增,在每個調度器的時鍾中斷時當前進程的虛擬運行時間都會累加。單純的說就是進程們都在比誰的vruntime最小,最小的將被調度。
- cfs_rq:此調度實體所處於的CFS運行隊列。
- my_q:如果此調度實體代表的是一個進程組,那么此調度實體就包含有一個自己的CFS運行隊列,其CFS運行隊列中存放的是此進程組中的進程,這些進程就不會在其他CFS運行隊列的紅黑樹中被包含(包括頂層紅黑樹也不會包含他們,他們只屬於這個進程組的紅黑樹)。
而在 struct task_struct 結構中,我們注意到有個調度類,里面包含的是調度處理函數,它具體如下:
1 struct sched_class { 2 /* 下一優先級的調度類 3 * 調度類優先級順序: stop_sched_class -> dl_sched_class -> rt_sched_class -> fair_sched_class -> idle_sched_class 4 */ 5 const struct sched_class *next; 6 7 /* 將進程加入到運行隊列中,即將調度實體(進程)放入紅黑樹中,並對 nr_running 變量加1 */ 8 void (*enqueue_task) (struct rq *rq, struct task_struct *p, int flags); 9 /* 從運行隊列中刪除進程,並對 nr_running 變量中減1 */ 10 void (*dequeue_task) (struct rq *rq, struct task_struct *p, int flags); 11 /* 放棄CPU,在 compat_yield sysctl 關閉的情況下,該函數實際上執行先出隊后入隊;在這種情況下,它將調度實體放在紅黑樹的最右端 */ 12 void (*yield_task) (struct rq *rq); 13 bool (*yield_to_task) (struct rq *rq, struct task_struct *p, bool preempt); 14 15 /* 檢查當前進程是否可被新進程搶占 */ 16 void (*check_preempt_curr) (struct rq *rq, struct task_struct *p, int flags); 17 18 /* 19 * It is the responsibility of the pick_next_task() method that will 20 * return the next task to call put_prev_task() on the @prev task or 21 * something equivalent. 22 * 23 * May return RETRY_TASK when it finds a higher prio class has runnable 24 * tasks. 25 */ 26 /* 選擇下一個應該要運行的進程運行 */ 27 struct task_struct * (*pick_next_task) (struct rq *rq, 28 struct task_struct *prev); 29 /* 將進程放回運行隊列 */ 30 void (*put_prev_task) (struct rq *rq, struct task_struct *p); 31 32 #ifdef CONFIG_SMP 33 /* 為進程選擇一個合適的CPU */ 34 int (*select_task_rq)(struct task_struct *p, int task_cpu, int sd_flag, int flags); 35 /* 遷移任務到另一個CPU */ 36 void (*migrate_task_rq)(struct task_struct *p, int next_cpu); 37 /* 用於上下文切換后 */ 38 void (*post_schedule) (struct rq *this_rq); 39 /* 用於進程喚醒 */ 40 void (*task_waking) (struct task_struct *task); 41 void (*task_woken) (struct rq *this_rq, struct task_struct *task); 42 /* 修改進程的CPU親和力(affinity) */ 43 void (*set_cpus_allowed)(struct task_struct *p, 44 const struct cpumask *newmask); 45 /* 啟動運行隊列 */ 46 void (*rq_online)(struct rq *rq); 47 /* 禁止運行隊列 */ 48 void (*rq_offline)(struct rq *rq); 49 #endif 50 /* 當進程改變它的調度類或進程組時被調用 */ 51 void (*set_curr_task) (struct rq *rq); 52 /* 該函數通常調用自 time tick 函數;它可能引起進程切換。這將驅動運行時(running)搶占 */ 53 void (*task_tick) (struct rq *rq, struct task_struct *p, int queued); 54 /* 在進程創建時調用,不同調度策略的進程初始化不一樣 */ 55 void (*task_fork) (struct task_struct *p); 56 /* 在進程退出時會使用 */ 57 void (*task_dead) (struct task_struct *p); 58 59 /* 用於進程切換 */ 60 void (*switched_from) (struct rq *this_rq, struct task_struct *task); 61 void (*switched_to) (struct rq *this_rq, struct task_struct *task); 62 /* 改變優先級 */ 63 void (*prio_changed) (struct rq *this_rq, struct task_struct *task, 64 int oldprio); 65 66 unsigned int (*get_rr_interval) (struct rq *rq, 67 struct task_struct *task); 68 69 void (*update_curr) (struct rq *rq); 70 71 #ifdef CONFIG_FAIR_GROUP_SCHED 72 void (*task_move_group) (struct task_struct *p, int on_rq); 73 #endif 74 };
這個調度類具體有什么用呢,實際上在內核中不同的調度算法它們的操作都不相同,為了方便修改、替換調度算法,使用了調度類,每個調度算法只需要實現自己的調度類就可以了,CFS算法有它的調度類,SCHED_FIFO也有它自己的調度類,當一個進程創建時,用什么調度算法就將其 task_struct->sched_class 指向其相應的調度類,調度器每次調度處理時,就通過當前進程的調度類函數進程操作,大大提高了可移植性和易修改性。
CFS運行隊列(struct cfs_rq)
對於 struct cfs_rq 結構沒有什么好說明的,只要確定其代表着一個CFS運行隊列,並且包含有一個紅黑樹進行選擇調度進程即可。
1 /* CFS調度的運行隊列,每個CPU的rq會包含一個cfs_rq,而每個組調度的sched_entity也會有自己的一個cfs_rq隊列 */ 2 struct cfs_rq { 3 /* CFS運行隊列中所有進程的總負載 */ 4 struct load_weight load; 5 /* 6 * nr_running: cfs_rq中調度實體數量 7 * h_nr_running: 只對進程組有效,其下所有進程組中cfs_rq的nr_running之和 8 */ 9 unsigned int nr_running, h_nr_running; 10 11 u64 exec_clock; 12 /* 當前CFS隊列上最小運行時間,單調遞增 13 * 兩種情況下更新該值: 14 * 1、更新當前運行任務的累計運行時間時 15 * 2、當任務從隊列刪除去,如任務睡眠或退出,這時候會查看剩下的任務的vruntime是否大於min_vruntime,如果是則更新該值。 16 */ 17 u64 min_vruntime; 18 #ifndef CONFIG_64BIT 19 u64 min_vruntime_copy; 20 #endif 21 /* 該紅黑樹的root */ 22 struct rb_root tasks_timeline; 23 /* 下一個調度結點(紅黑樹最左邊結點,最左邊結點就是下個調度實體) */ 24 struct rb_node *rb_leftmost; 25 26 /* 27 * 'curr' points to currently running entity on this cfs_rq. 28 * It is set to NULL otherwise (i.e when none are currently running). 29 */ 30 /* 31 * curr: 當前正在運行的sched_entity(對於組雖然它不會在cpu上運行,但是當它的下層有一個task在cpu上運行,那么它所在的cfs_rq就把它當做是該cfs_rq上當前正在運行的sched_entity) 32 * next: 表示有些進程急需運行,即使不遵從CFS調度也必須運行它,調度時會檢查是否next需要調度,有就調度next 33 * 34 * skip: 略過進程(不會選擇skip指定的進程調度) 35 */ 36 struct sched_entity *curr, *next, *last, *skip; 37 38 #ifdef CONFIG_SCHED_DEBUG 39 unsigned int nr_spread_over; 40 #endif 41 42 #ifdef CONFIG_SMP 43 /* 44 * CFS Load tracking 45 * Under CFS, load is tracked on a per-entity basis and aggregated up. 46 * This allows for the description of both thread and group usage (in 47 * the FAIR_GROUP_SCHED case). 48 */ 49 unsigned long runnable_load_avg, blocked_load_avg; 50 atomic64_t decay_counter; 51 u64 last_decay; 52 atomic_long_t removed_load; 53 54 #ifdef CONFIG_FAIR_GROUP_SCHED 55 /* Required to track per-cpu representation of a task_group */ 56 u32 tg_runnable_contrib; 57 unsigned long tg_load_contrib; 58 59 /* 60 * h_load = weight * f(tg) 61 * 62 * Where f(tg) is the recursive weight fraction assigned to 63 * this group. 64 */ 65 unsigned long h_load; 66 u64 last_h_load_update; 67 struct sched_entity *h_load_next; 68 #endif /* CONFIG_FAIR_GROUP_SCHED */ 69 #endif /* CONFIG_SMP */ 70 71 #ifdef CONFIG_FAIR_GROUP_SCHED 72 /* 所屬於的CPU rq */ 73 struct rq *rq; /* cpu runqueue to which this cfs_rq is attached */ 74 75 /* 76 * leaf cfs_rqs are those that hold tasks (lowest schedulable entity in 77 * a hierarchy). Non-leaf lrqs hold other higher schedulable entities 78 * (like users, containers etc.) 79 * 80 * leaf_cfs_rq_list ties together list of leaf cfs_rq's in a cpu. This 81 * list is used during load balance. 82 */ 83 int on_list; 84 struct list_head leaf_cfs_rq_list; 85 /* 擁有該CFS運行隊列的進程組 */ 86 struct task_group *tg; /* group that "owns" this runqueue */ 87 88 #ifdef CONFIG_CFS_BANDWIDTH 89 int runtime_enabled; 90 u64 runtime_expires; 91 s64 runtime_remaining; 92 93 u64 throttled_clock, throttled_clock_task; 94 u64 throttled_clock_task_time; 95 int throttled, throttle_count; 96 struct list_head throttled_list; 97 #endif /* CONFIG_CFS_BANDWIDTH */ 98 #endif /* CONFIG_FAIR_GROUP_SCHED */ 99 };
- load:其保存的是進程組中所有進程的權值總和,需要注意子進程計算vruntime時需要用到進程組的load。
CPU運行隊列(struct rq)
每個CPU都有自己的 struct rq 結構,其用於描述在此CPU上所運行的所有進程,其包括一個實時進程隊列和一個根CFS運行隊列,在調度時,調度器首先會先去實時進程隊列找是否有實時進程需要運行,如果沒有才會去CFS運行隊列找是否有進行需要運行,這就是為什么常說的實時進程優先級比普通進程高,不僅僅體現在prio優先級上,還體現在調度器的設計上,至於dl運行隊列,我暫時還不知道有什么用處,其優先級比實時進程還高,但是創建進程時如果創建的是dl進程創建會錯誤(具體見sys_fork)。
1 /* CPU運行隊列,每個CPU包含一個struct rq */ 2 struct rq { 3 /* 處於運行隊列中所有就緒進程的load之和 */ 4 raw_spinlock_t lock; 5 6 /* 7 * nr_running and cpu_load should be in the same cacheline because 8 * remote CPUs use both these fields when doing load calculation. 9 */ 10 /* 此CPU上總共就緒的進程數,包括cfs,rt和正在運行的 */ 11 unsigned int nr_running; 12 #ifdef CONFIG_NUMA_BALANCING 13 unsigned int nr_numa_running; 14 unsigned int nr_preferred_running; 15 #endif 16 #define CPU_LOAD_IDX_MAX 5 17 /* 根據CPU歷史情況計算的負載,cpu_load[0]一直等於load.weight,當達到負載平衡時,cpu_load[1]和cpu_load[2]都應該等於load.weight */ 18 unsigned long cpu_load[CPU_LOAD_IDX_MAX]; 19 /* 最后一次更新 cpu_load 的時間 */ 20 unsigned long last_load_update_tick; 21 #ifdef CONFIG_NO_HZ_COMMON 22 u64 nohz_stamp; 23 unsigned long nohz_flags; 24 #endif 25 #ifdef CONFIG_NO_HZ_FULL 26 unsigned long last_sched_tick; 27 #endif 28 /* 是否需要更新rq的運行時間 */ 29 int skip_clock_update; 30 31 /* capture load from *all* tasks on this cpu: */ 32 /* CPU負載,該CPU上所有可運行進程的load之和,nr_running更新時這個值也必須更新 */ 33 struct load_weight load; 34 unsigned long nr_load_updates; 35 /* 進行上下文切換次數,只有proc會使用這個 */ 36 u64 nr_switches; 37 38 /* cfs調度運行隊列,包含紅黑樹的根 */ 39 struct cfs_rq cfs; 40 /* 實時調度運行隊列 */ 41 struct rt_rq rt; 42 struct dl_rq dl; 43 44 #ifdef CONFIG_FAIR_GROUP_SCHED 45 /* list of leaf cfs_rq on this cpu: */ 46 struct list_head leaf_cfs_rq_list; 47 48 struct sched_avg avg; 49 #endif /* CONFIG_FAIR_GROUP_SCHED */ 50 51 /* 52 * This is part of a global counter where only the total sum 53 * over all CPUs matters. A task can increase this counter on 54 * one CPU and if it got migrated afterwards it may decrease 55 * it on another CPU. Always updated under the runqueue lock: 56 */ 57 /* 曾經處於隊列但現在處於TASK_UNINTERRUPTIBLE狀態的進程數量 */ 58 unsigned long nr_uninterruptible; 59 60 /* 61 * curr: 當前正在此CPU上運行的進程 62 * idle: 當前CPU上idle進程的指針,idle進程用於當CPU沒事做的時候調用,它什么都不執行 63 */ 64 struct task_struct *curr, *idle, *stop; 65 /* 下次進行負載平衡執行時間 */ 66 unsigned long next_balance; 67 /* 在進程切換時用來存放換出進程的內存描述符地址 */ 68 struct mm_struct *prev_mm; 69 70 /* rq運行時間 */ 71 u64 clock; 72 u64 clock_task; 73 74 atomic_t nr_iowait; 75 76 #ifdef CONFIG_SMP 77 struct root_domain *rd; 78 /* 當前CPU所在基本調度域,每個調度域包含一個或多個CPU組,每個CPU組包含該調度域中一個或多個CPU子集,負載均衡都是在調度域中的組之間完成的,不能跨域進行負載均衡 */ 79 struct sched_domain *sd; 80 81 unsigned long cpu_capacity; 82 83 unsigned char idle_balance; 84 /* For active balancing */ 85 int post_schedule; 86 /* 如果需要把進程遷移到其他運行隊列,就需要設置這個位 */ 87 int active_balance; 88 int push_cpu; 89 struct cpu_stop_work active_balance_work; 90 91 /* 該運行隊列所屬CPU */ 92 int cpu; 93 int online; 94 95 struct list_head cfs_tasks; 96 97 u64 rt_avg; 98 /* 該運行隊列存活時間 */ 99 u64 age_stamp; 100 u64 idle_stamp; 101 u64 avg_idle; 102 103 /* This is used to determine avg_idle's max value */ 104 u64 max_idle_balance_cost; 105 #endif 106 107 #ifdef CONFIG_IRQ_TIME_ACCOUNTING 108 u64 prev_irq_time; 109 #endif 110 #ifdef CONFIG_PARAVIRT 111 u64 prev_steal_time; 112 #endif 113 #ifdef CONFIG_PARAVIRT_TIME_ACCOUNTING 114 u64 prev_steal_time_rq; 115 #endif 116 117 /* calc_load related fields */ 118 /* 用於負載均衡 */ 119 unsigned long calc_load_update; 120 long calc_load_active; 121 122 #ifdef CONFIG_SCHED_HRTICK 123 #ifdef CONFIG_SMP 124 int hrtick_csd_pending; 125 struct call_single_data hrtick_csd; 126 #endif 127 /* 調度使用的高精度定時器 */ 128 struct hrtimer hrtick_timer; 129 #endif 130 131 #ifdef CONFIG_SCHEDSTATS 132 /* latency stats */ 133 struct sched_info rq_sched_info; 134 unsigned long long rq_cpu_time; 135 /* could above be rq->cfs_rq.exec_clock + rq->rt_rq.rt_runtime ? */ 136 137 /* sys_sched_yield() stats */ 138 unsigned int yld_count; 139 140 /* schedule() stats */ 141 unsigned int sched_count; 142 unsigned int sched_goidle; 143 144 /* try_to_wake_up() stats */ 145 unsigned int ttwu_count; 146 unsigned int ttwu_local; 147 #endif 148 149 #ifdef CONFIG_SMP 150 struct llist_head wake_list; 151 #endif 152 153 #ifdef CONFIG_CPU_IDLE 154 /* Must be inspected within a rcu lock section */ 155 struct cpuidle_state *idle_state; 156 #endif 157 };