閱讀311 返回首頁    go 阿裏雲 go 技術社區[雲棲]


調度子係統1_調度子係統初始化

unsigned int sysctl_sched_rt_period = 1000000;
int sysctl_sched_rt_runtime = 950000;

//	參考:
//		SMP負載均衡
//		https://soft.chinabyte.com/os/22/12359522.shtml
//		linux組調度淺析
//		https://hi.baidu.com/_kouu/item/0fe32610e493314be75e06d1
//		進程調度和組調度 
//		https://blog.chinaunix.net/uid-27052262-id-3239263.html
//		CF調度器
//		https://blog.csdn.net/wudongxu/article/details/8574749
//		FIFO調度器
//		https://lwn.net/Articles/296419/

//	調度初始化
//	函數任務:
//		1.初始化rootdomain
//			rootdomain指示rq可運行的cpu集合
//		2.初始化real-time task對cpu的占有率
//			sysctl_sched_rt_period代表rt進程的調度周期
//			sysctl_sched_rt_runtime代表rt進程在調度周期中可運行的時間
//		3.初始化per-cpu rq
//			3.1 初始化公平調度隊列,實時調度隊列
//			3.2 初始化cpu負載記錄數組
//			3.3 初始化cpu使用的tick hrtimer
//		4.初始化current(init_task)為idle task
//			4.1 設置current由公平調度管理
1.1 void __init sched_init(void)
{
	int i, j;

#ifdef CONFIG_SMP
	//初始化默認的調度域
	init_defrootdomain();
#endif

	//rt_bandwidth表示實時進程對cpu的占有率
	init_rt_bandwidth(&def_rt_bandwidth,
			global_rt_period(), global_rt_runtime());

	//初始化per-cpu rq
	for_each_possible_cpu(i) {
		struct rq *rq;
		//per-cpu 運行隊列
		rq = cpu_rq(i);
		raw_spin_lock_init(&rq->lock);
		rq->nr_running = 0;
		rq->calc_load_active = 0;
		rq->calc_load_update = jiffies + LOAD_FREQ;
		//初始化公平調度策略、實時調度策略隊列
		init_cfs_rq(&rq->cfs, rq);
		init_rt_rq(&rq->rt, rq);
		//調度隊列中,實時進程對cpu的占有率
		rq->rt.rt_runtime = def_rt_bandwidth.rt_runtime;
		//分5個等級記錄cpu的負載情況
		for (j = 0; j < CPU_LOAD_IDX_MAX; j++)
			rq->cpu_load[j] = 0;
#ifdef CONFIG_SMP
		rq->sd = NULL;
		rq->rd = NULL;
		rq->post_schedule = 0;
		rq->active_balance = 0;
		rq->next_balance = jiffies;
		rq->push_cpu = 0;
		//rq運行的cpu
		rq->cpu = i;
		rq->online = 0;
		rq->migration_thread = NULL;
		rq->idle_stamp = 0;
		rq->avg_idle = 2*sysctl_sched_migration_cost;
		INIT_LIST_HEAD(&rq->migration_queue);
		rq_attach_root(rq, &def_root_domain);
#endif
		//初始化rq使用的hrtimer
		init_rq_hrtick(rq);
		atomic_set(&rq->nr_iowait, 0);
	}

	set_load_weight(&init_task);


	//load balancing軟中斷
#ifdef CONFIG_SMP
	open_softirq(SCHED_SOFTIRQ, run_rebalance_domains);
#endif

	atomic_inc(&init_mm.mm_count);
	//通知底層體係結構不需要切換虛擬地址空間的用戶空間部分
	enter_lazy_tlb(&init_mm, current);

	//將當前進程,即init_task更新為idle thread
	init_idle(current, smp_processor_id());
	//下次進行load balancing的時間戳
	calc_load_update = jiffies + LOAD_FREQ;
	//當前進程關聯的調度累
	current->sched_class = &fair_sched_class;

	perf_event_init();
	//標識調度器開始運行
	scheduler_running = 1;
}
//	rq結構分析
//	參考 https://blog.csdn.net/bullbat/article/details/7160246
1.2 struct rq {
	
	raw_spinlock_t lock;
	//就緒隊列中進程數
	unsigned long nr_running;
	#define CPU_LOAD_IDX_MAX 5
	//分5個等級記錄就緒隊列的負載情況
	//在係統初始化的時候sched_init把rq的cpu_load array初始化為0,之後通過函數update_cpu_load
	//公式如下:
    //	cpu_load[0]等於rq中load.weight的值
    //	cpu_load[1]=(cpu_load[1]*(2-1)+cpu_load[0])/2 
    //	cpu_load[2]=(cpu_load[2]*(4-1)+cpu_load[0])/4 
    //	cpu_load[3]=(cpu_load[3]*(8-1)+cpu_load[0])/8 
    //	cpu_load[4]=(cpu_load[4]*(16-1)+cpu_load[0]/16 
    //通過this_cpu_load返回的cpu load值是cpu_load[0]
    //進行cpu blance或migration時,通過調用source_load target_load取得對該處理器cpu_load index值。
	unsigned long cpu_load[CPU_LOAD_IDX_MAX];
	//本schedule entity的load->weight的總和
	struct load_weight load;
	//scheduler tick中調用update_cpu_load時,這個值就增加一,可以用來反饋目前cpu load更新的次數。
	unsigned long nr_load_updates;
	//統計處理器context switch次數
	//	schedule進行累加,並可以通過函數nr_context_switches統計目前所有處理器總共的context switch次數
	//	或是可以查看/proc/stat中的ctxt獲取目前整個係統觸發context switch的次數。
	u64 nr_switches;
	//公平調度隊列
	struct cfs_rq cfs;
	//實時調度隊列
	struct rt_rq rt;

//支持group cfs tasks的機製
#ifdef CONFIG_FAIR_GROUP_SCHED
	//1.fair group scheduling, 
    //	將cfs rq中若幹task組織成若幹task group,即子cfs_rq,屬於這group的task所使用到的處理器時間就會以個group總共所分的的時間為上限。 
    //2.基於cgroup的fair group scheduling 架構
    //	2.1 可以創造出有階層性的task組織,根據不同task的功能群組化。 
    //	2.2 在配置給該群組對應的處理器資源,讓屬於該群組下的task可以通過rq機製使用屬於該群組下的資源。 
    //3.加入、退出、遍曆leaf_cfs_rq_list
    // 	3.1 加入:list_add_leaf_cfs_rq把一個group cfs rq加入。
    //	3.2 退出:list_del_leaf_cfs_rq把一個group cfs rq退出。
    //	3.3 遍曆:for_each_leaf_cfs_rq遍曆rq上得所有leaf cfs_rq 
	struct list_head leaf_cfs_rq_list;
#endif

#ifdef CONFIG_RT_GROUP_SCHED
	//類似leaf_cfs_rq_list,隻是這裏是針對屬於real-time的task,對應的操作函數有list_add_leaf_rt_rq,
	//list_del_leaf_rt_rq, for_each_leaf_rt_rq.  
	struct list_head leaf_rt_rq_list;
#endif
	//統計目前rq中有多少task屬於TASK_UNINTERRUPTIBLE的狀態.
	unsigned long nr_uninterruptible;
	//指向目前處理器正在執行的task; 沒有可運行task時,運行idle task.
	struct task_struct *curr, *idle;
	//基於處理器的jiffies值,用以記錄下次進行處理器balancing的時間點.
	unsigned long next_balance;
	//用以存儲context-switch發生時,前一個task的memory management結構.
	struct mm_struct *prev_mm;
	//記錄目前rq的clock值
	//	該值等於調用sched_clock_cpu(cpu_of(rq))的返回值,在scheduler_tick中通過函數update_rq_clock更新.
	u64 clock;
	//記錄目前rq中處於等待i/o狀態的task數。
	//	例如當driver接受來自task的調用,但處於等待i/o階段時,為了充分利用處理器的執行資源,
	//	這時就可以在driver中調用函數io_schedule,此時就會把目前rq中的nr_iowait加一,
    //	並設定目前task的io_wait為1, 然後觸發scheduling 讓其他task有機會可以得到處理器執行時間。
	atomic_t nr_iowait;

#ifdef CONFIG_SMP
	//root domain是基於多核心架構下的機製, 
    //其中包括了: 
    //	cpu mask(包括span,online,rt overload), 
    //	reference count 
    //	cpupri 
    //1.當root domain被rq引用時,refcount加一,反之就減一。
    //2.cpu mask span表示rq可運行的cpu mask、 noline為已經被rq安排了進程的cpu
    //3.當rq中real-time的task執行完畢時,會調用函數pull_rt_task從該rq rto_mask中標識的cpu上,
    //	查找是否有處理器有大於一個以上的real-time task,若有就會遷移到本cpu執行。
    //4.cpupri不同於task的優先級,cpupri本身有102個優先級:
    //	-1 invalid, 
    //	 0 idle
    //	 1 normal
    //	2-101對應real-time priority 0-99 
    //參考convert_prio, task priority如果是140就會對應到cpu idle,如果是大於等於100就會對應到cpu normal, 
    //	若是task priority介於0-99之間,就會對應到cpu real-time priority 101-2 之間。  
    //在實際的操作上, 可以調用cpupri_find傳入一個real-time task結構,此時就會依據cpupri中pri_to_cpu選擇
    //	一個目前執行real-time task且該task的優先級比目前要插入的task更低的處理器, 並通過cpu mask(lowest_mask)
    //	返回目前可以選擇的處理器mask。 
    //參考 kernel/sched_cpupri.c. 
    //在初始化的過程中,通過sched_init調用init_defrootdomain對root domain與cpu priority機製進行初始化。
	struct root_domain *rd;
	//schedule domain是基於多核心架構下的機製。
    //每個處理器都會有一個默認的scheduling domain.
    //	1.scheduling domain的層次結構:
    //		通過parent找到上一層的domain。
    //		通過child找到下一層的domain(NULL表示結尾)。
    //	2.scheduling domain覆蓋的處理器:
    //		通過span掩碼,表示這個domain所覆蓋的處理器範圍。
    //	3.scheduling domain的根:
    //		base domain涵蓋係統中所有處理器。
    //	4.scheduling doma的cpu group:
    //		4.1 schedule domain都會包括一個或一個以上的cpu groups(結構為struct sched_group),並通過next指針把cpu groups串聯在一起(成為一個單向的circular linked list)。 
    //		4.2 cpu group都通過cpumask來定義其所涵蓋的處理器,並且cpu group所包括的處理器範圍必需涵蓋在所屬的schedule domain處理器範圍中。
    //		4.3 當scheduling domain在balancing時,會以其下的cpu groups為單位,通過cpu_power(該group所涵蓋的處理器的tasks loading 
    //			總和)來比較不同的cpu groups的負荷來進行tasks的移動,達到balancing的目的。 
    //	5.loadbalance時機:
    //		5.1 在sched_init中, 通過open_softirq注冊SCHED_SOFTIRQ軟中斷\
    //		5.2 在scheduler_tick中,通過trigger_load_balance確認目前的jiffies值是否大於要觸發load balance的時間戳,並通過raise_softirq觸發SCHED_SOFTIRQ。
    //		5.3 在load balance軟中斷中,通過run_rebalance_domains進行scheduling domain load balance. 
    //	有關scheduling domain進一步的內容,參考 
    //	Documentation/scheduler/sched-domains.txt.   
	struct sched_domain *sd;
	//為1表示目前cpu rq中執行的為idle task
	//為0表示執行非idle task 
	unsigned char idle_at_tick;

	int post_schedule;

	//為1表示這個rq正在運行fair scheduling的load balance,此時會調用stop_one_cpu_nowait暫停該cpu的進程
    //然後通過調用active_load_balance_cpu_stop把tasks從最忙碌的處理器移到idle的處理器上執行
	int active_balance;
	//用以存儲目前進入idle狀態並且進行load balance流程的處理器id。
	//整個流程為
	//	進程調用schedule時, 若該處理器rq的nr_running為0(也就是目前沒有正在執行的task),就會調用idle_balance並
	//	觸發後續load balance流程
	int push_cpu;
	//用以存儲目前運作這個rq的處理器id
	int cpu;
	int online;
	//如果rq中目前有task正在執行,這個值會等於目前該rq的load weight除以目前rq中task數目的均值
	unsigned long avg_load_per_task;


	struct task_struct *migration_thread;
	struct list_head migration_queue;
	//統計目前real-time task執行時間的均值, 反應目前係統中real-time task平均被分配到的執行時間值
	u64 rt_avg;
	u64 age_stamp;
	//表示cpu進入idle狀態的時間
	u64 idle_stamp;
	u64 avg_idle;
#endif

	//用以記錄下一次計算cpu load的時間,初始值為當前的jiffies加上五秒與1次的scheduling tick的間隔
	unsigned long calc_load_update;
	long calc_load_active;

#ifdef CONFIG_SCHED_HRTICK
#ifdef CONFIG_SMP

	int hrtick_csd_pending;
	struct call_single_data hrtick_csd;
#endif
	struct hrtimer hrtick_timer;
#endif

};

最後更新:2017-04-03 14:54:43

  上一篇:go 最牛B的編碼套路
  下一篇:go 時間子係統15_獲取係統時間