linux排程器原始碼分析 初始化 二

2021-07-03 03:18:19 字數 4243 閱讀 4735

上期文章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...