上期文章linux排程器原始碼分析 - 概述(一)已經把排程器相關的資料結構介紹了一遍,本篇著重通過**說明排程器在系統啟動初始化階段是如何初始化和工作的。通過上期文章我們知道,在多核cpu和smp系統中,每個cpu(多核cou中的每個核)都有自己的struct rq佇列,而rq佇列中又有著自己的struct cfs_rq和struct rt_rq。在初始化時就是對這三個結構進行初始化。
當linux啟動時,最先會通過彙編**進行硬體和cpu的初始化,最後會跳轉到c**,而最初跳轉到的c**入口為
在start_kerenl函式中,進行了系統啟動過程中幾乎所有重要的初始化(有一部分在boot中初始化,有一部分在start_kernel之前的彙編**進行初始化),包括記憶體、頁表、必要資料結構、訊號、排程器、硬體裝置等。而這些初始化是由誰來負責的?就是由init_task這個程序。init_task是靜態定義的乙個程序,也就是說當核心被放入記憶體時,它就已經存在,它沒有自己的使用者空間,一直處於核心空間中執行,並且也只處於核心空間執行。當它執行到最後,將start_kernel中所有的初始化執行完成後,會在核心中啟動乙個kernel_init核心執行緒和乙個kthreadd核心執行緒,kernel_init核心執行緒執行到最後會通過execve系統呼叫執行轉變為我們所熟悉的init程序,而kthreadd核心執行緒是核心用於管理排程其他的核心執行緒的守護執行緒。在最後init_task將變成乙個idle程序,用於在cpu沒有程序執行時執行它,它在此時僅僅用於空轉。
在start_kernel中對排程器進行初始化的函式就是sched_init,其主要工作為
需要說明的是init_task在這裡會被轉變為idle程序,但是它還會繼續執行初始化工作,相當於這裡只是給init_task掛個idle程序的名號,它其實還是init_task程序,只有到最後init_task程序開啟了kernel_init和kthreadd程序之後,才轉變為真正意義上的idle程序。
1排程器的初始化還是比較簡單的,畢竟排程器的核心不在此,重頭戲在它的執行時處理,之後的文章會詳細分析排程器的執行時處理。/***路徑: 核心源**目錄/kernel/sched/core.c */2
3/*執行到此時核心只有乙個程序init_task,current就為init_task。之後的init程序在初始化到最後的rest_init中啟動 */4
void __init sched_init(void)5
49#endif /* config_cpumask_offstack */50}
51/*
初始化實時程序的頻寬限制,用於設定實時程序在cpu中所占用比的
*/52 init_rt_bandwidth(&def_rt_bandwidth,
53global_rt_period(), global_rt_runtime());
54 init_dl_bandwidth(&def_dl_bandwidth,
55global_rt_period(), global_rt_runtime());
5657
#ifdef config_smp
58/*
初始化預設的排程域,排程域包含乙個或多個cpu,負載均衡是在排程域內執行的,相互之間隔離
*/59
init_defrootdomain();
60#endif
6162
#ifdef config_rt_group_sched
63/*
初始化實時程序的頻寬限制,用於設定實時程序在cpu中所占用比的
*/64 init_rt_bandwidth(&root_task_group.rt_bandwidth,
65global_rt_period(), global_rt_runtime());
66#endif /* config_rt_group_sched */
6768
#ifdef config_cgroup_sched
69/*
將分配好空間的 root_task_group 加入 task_groups 鍊錶
*/70 list_add(&root_task_group.list, &task_groups);
71 init_list_head(&root_task_group.children);
72 init_list_head(&root_task_group.siblings);
73/*
自動分組初始化,每個tty(控制台)動態的建立程序組,這樣就可以降低高負載情況下的桌面延遲
*/74 autogroup_init(&init_task);
7576
#endif /* config_cgroup_sched */
77/*
遍歷設定每乙個cpu
*/78
for_each_possible_cpu(i)
163/*
設定 init_task 程序的權重
*/164 set_load_weight(&init_task);
165166
#ifdef config_preempt_notifiers
167/*
初始化通知鏈
*/168 init_hlist_head(&init_task.preempt_notifiers);
169#endif
170171
/*172
* the boot idle thread does lazy mmu switching as well:
173*/
174 atomic_inc(&init_mm.mm_count);
175 enter_lazy_tlb(&init_mm, current);
176177
/*178
* make us the idle thread. technically, schedule() should not be
179* called from this thread, however somewhere below it might be,
180* but because we are the idle thread, we just pick up running again
181* when this runqueue becomes "idle".
182*/
183/*
將當前程序初始化為idle程序,idle程序用於當cpu沒有程序可執行時執行,空轉
*/184
init_idle(current, smp_processor_id());
185/*
下次負載更新時間(是乙個相對時間)
*/186 calc_load_update = jiffies +load_freq;
187188
/*189
* during early bootup we pretend to be a normal task:
190*/
191/*
設定idle程序使用cfs排程策略
*/192 current->sched_class = &fair_sched_class;
193194
#ifdef config_smp
195 zalloc_cpumask_var(&sched_domains_tmpmask, gfp_nowait);
196/*
may be allocated at isolcpus cmdline parse time
*/197
if (cpu_isolated_map ==null)
198 zalloc_cpumask_var(&cpu_isolated_map, gfp_nowait);
199idle_thread_set_boot_cpu();
200set_cpu_rq_start_time();
201#endif
202init_sched_fair_class();
203/*
這裡只是標記排程器開始執行了,但是此時系統只有乙個init_task(idle)程序,並且定時器都還沒啟動。並不會排程到其他程序,也沒有其他程序可供排程
*/204 scheduler_running = 1
;205 }
linux排程器原始碼分析 初始化 二
上期文章linux排程器原始碼分析 概述 一 已經把排程器相關的資料結構介紹了一遍,本篇著重通過 說明排程器在系統啟動初始化階段是如何初始化和工作的。通過上期文章我們知道,在多核cpu和smp系統中,每個cpu 多核cou中的每個核 都有自己的struct rq佇列,而rq佇列中又有著自己的stru...
Linux程序排程0 11原始碼
void schedule void 排程函式if p signal blockable p blocked p state task interruptible 看訊號是否遮蔽掉,且是否處於task interruptible狀態 p state task running task running...
位元幣原始碼分析 任務排程器的使用
bitcoin 程序啟動後,有乙個專門的執行緒做任務排程,這些任務根據指定的時刻,執行對應的函式 排程器類主要是實現了乙個生產者消費者的任務佇列,只是這個任務佇列是用 std multimap 實現的,map 的key表達某一時刻,map的值表達 那一時刻要執行的函式,內部使用條件變數和鎖來保護mu...