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