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

2021-09-20 22:30:32 字數 4511 閱讀 8106

上期文章linux排程器原始碼分析 - 概述(一)

已經把排程器相關的資料結構介紹了一遍,本篇著重通過**說明排程器在系統啟動初始化階段是如何初始化和工作的。通過上期文章我們知道,在多核cpu和smp系統中,每個cpu(多核cou中的每個核)都有自己的struct rq佇列,而rq佇列中又有著自己的struct cfs_rq和struct rt_rq。在初始化時就是對這三個結構進行初始化。

當linux啟動時,最先會通過彙編**進行硬體和cpu的初始化,最後會跳轉到c**,而最初跳轉到的c**入口為

/

/ asmlinkage __visible void __init start_kernel(void)

在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,其主要工作為

對相關資料結構分配記憶體 初始化root_task_group 初始化每個cpu的rq佇列(包括其中的cfs佇列和實時程序佇列) 將init_task程序轉變為idle程序需要說明的是init_task在這裡會被轉變為idle程序,但是它還會繼續執行初始化工作,相當於這裡只是給init_task掛個idle程序的名號,它其實還是init_task程序,只有到最後init_task程序開啟了kernel_init和kthreadd程序之後,才轉變為真正意義上的idle程序。

/

* **路徑: 核心源**目錄/kernel/sched/core.c *//

* 執行到此時核心只有乙個程序init_task,current就為init_task。之後的init程序在初始化到最後的rest_init中啟動 *

/void __init sched_init(void)

#endif /

* config_cpumask_offstack */}

/* 初始化實時程序的頻寬限制,用於設定實時程序在cpu中所占用比的 *

/init_rt_bandwidth(

&def_rt_bandwidth,

global_rt_period(

), global_rt_runtime())

;init_dl_bandwidth(

&def_dl_bandwidth,

global_rt_period(

), global_rt_runtime())

;#ifdef config_smp

/* 初始化預設的排程域,排程域包含乙個或多個cpu,負載均衡是在排程域內執行的,相互之間隔離 *

/init_defrootdomain();

#endif

#ifdef config_rt_group_sched

/* 初始化實時程序的頻寬限制,用於設定實時程序在cpu中所占用比的 *

/init_rt_bandwidth(

&root_task_group.rt_bandwidth,

global_rt_period(

), global_rt_runtime())

;#endif /

* config_rt_group_sched *

/#ifdef config_cgroup_sched

/* 將分配好空間的 root_task_group 加入 task_groups 鍊錶 *

/list_add(

&root_task_group.list,

&task_groups)

;init_list_head(

&root_task_group.children)

;init_list_head(

&root_task_group.siblings);/

* 自動分組初始化,每個tty(控制台)動態的建立程序組,這樣就可以降低高負載情況下的桌面延遲 *

/autogroup_init(

&init_task)

;#endif /

* config_cgroup_sched *//

* 遍歷設定每乙個cpu *

/for_each_possible_cpu(i)

/* 設定 init_task 程序的權重 *

/set_load_weight(

&init_task)

;#ifdef config_preempt_notifiers

/* 初始化通知鏈 *

/init_hlist_head(

&init_task.preempt_notifiers)

;#endif/*

* the boot idle thread does lazy mmu switching as well:*/

atomic_inc(

&init_mm.mm_count)

;enter_lazy_tlb(

&init_mm, current);/

** make us the idle thread. technically, schedule(

) should not be

* called from this thread, however somewhere below it might be,

* but because we are the idle thread, we just pick up running again

* when this runqueue becomes "idle".*

//* 將當前程序初始化為idle程序,idle程序用於當cpu沒有程序可執行時執行,空轉 *

/init_idle(current, smp_processor_id())

;/* 下次負載更新時間(是乙個相對時間)*/

calc_load_update = jiffies + load_freq;/*

* during early bootup we pretend to be a normal task:*/

/* 設定idle程序使用cfs排程策略 *

/current-

>sched_class =

&fair_sched_class;

#ifdef config_smp

zalloc_cpumask_var(

&sched_domains_tmpmask, gfp_nowait);/

* may be allocated at isolcpus cmdline parse time*/

if(cpu_isolated_map =

=null

)zalloc_cpumask_var(

&cpu_isolated_map, gfp_nowait)

;idle_thread_set_boot_cpu();

set_cpu_rq_start_time();

#endif

init_sched_fair_class();

/* 這裡只是標記排程器開始執行了,但是此時系統只有乙個init_task(idle)程序,並且定時器都還沒啟動。並不會排程到其他程序,也沒有其他程序可供排程 *

/scheduler_running = 1;

}

排程器的初始化還是比較簡單的,畢竟排程器的核心不在此,重頭戲在它的執行時處理,之後的文章會詳細分析排程器的執行時處理。

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...