【调度器初始化】【sched_init】

0号进程初始化调度器相关的结构体,并将0号进程调度类变换为idle_sched_class调度类

Kernel_init------------》Sched_init

​
#ifdef CONFIG_CGROUP_SCHED
/*
 * Default task group.
 * Every task in system belongs to this group at bootup.
 */
struct task_group root_task_group;
LIST_HEAD(task_groups);
#endif
struct rt_bandwidth def_rt_bandwidth;
#define ROOT_TASK_GROUP_LOAD	NICE_0_LOAD

void __init sched_init(void)
{
	int i, j;
	unsigned long alloc_size = 0, ptr;
// 这个宏定义的Y
#ifdef CONFIG_FAIR_GROUP_SCHED
	/*为cfs_rq,sched_entity分配空间,每个cpu都存在cfs_rq和se*/
	alloc_size += 2 * nr_cpu_ids * sizeof(void **);
#endif
// 这个宏定义的Y
#ifdef CONFIG_RT_GROUP_SCHED
	alloc_size += 2 * nr_cpu_ids * sizeof(void **);
#endif
	if (alloc_size) {
        // kzalloc分配空间并初始化为0
//%GFP_NOWAIT - Allocation will not sleep.
        // ptr返回的是首地址
		ptr = (unsigned long)kzalloc(alloc_size, GFP_NOWAIT);

#ifdef CONFIG_FAIR_GROUP_SCHED
// task_group是一个双向链表吗?
/*root_task_group作为RB tree的根节点,作为所有task的
       跟节点(不管是单个task还是进程组task) 根据上面alloc_size地址空间大小和
        ptr作为alloc_size这块空间大小的首地址进行分配
        可以看到分别每个cpu上的cfs_rq和se分配空间,*/
// 设置root_task_group中调度实体的指
		root_task_group.se = (struct sched_entity **)ptr;
		ptr += nr_cpu_ids * sizeof(void **);
// 设置root_task_group中CFS运行队列的指针
		root_task_group.cfs_rq = (struct cfs_rq **)ptr;
		ptr += nr_cpu_ids * sizeof(void **);

#endif /* CONFIG_FAIR_GROUP_SCHED */
#ifdef CONFIG_RT_GROUP_SCHED
/*同上,为rt相关分配空间,也就是说root_task_group包含了cfs task
         rt task*/
// 设置root_task_group中实时调度实体的指针
		root_task_group.rt_se = (struct sched_rt_entity **)ptr;
		ptr += nr_cpu_ids * sizeof(void **);
// 设置root_task_group中实时运行队列的指针
		root_task_group.rt_rq = (struct rt_rq **)ptr;
		ptr += nr_cpu_ids * sizeof(void **);

#endif /* CONFIG_RT_GROUP_SCHED */
	}
#ifdef CONFIG_CPUMASK_OFFSTACK // 这个宏是没有定义的
	for_each_possible_cpu(i) {
		per_cpu(load_balance_mask, i) = (cpumask_var_t)kzalloc_node(
        cpumask_size(), GFP_KERNEL, cpu_to_node(i));
	}
#endif /* CONFIG_CPUMASK_OFFSTACK */

/*初始化rt thread的带宽限制,以1s一个周期计算,如果rt在rt_rq里面运行时间超过
   950ms则强制将task出rt_rq队列,等到下一个1s周期再次运行,每次循环往复知道thread
    运行完毕*/
	init_rt_bandwidth(&def_rt_bandwidth,
			global_rt_period(), global_rt_runtime());
	/*意思同上,dl thread不知道是什么thread 是deadline thread*/
	init_dl_bandwidth(&def_dl_bandwidth,
			global_rt_period(), global_rt_runtime());

#ifdef CONFIG_SMP
// 初始化默认的调度域
/*更新root domain,并初始化max_cpu_capacity结构体,update_cpu_capacity时候
    会被使用到*/
	init_defrootdomain();
#endif

#ifdef CONFIG_RT_GROUP_SCHED
/*为root_task_group内的rt task设置带宽限制*/ 
	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 group链表中,并设置它的孩子节点和兄弟节点链表*/
// 将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);
// 初始化init_task->signal->autogroup
	autogroup_init(&init_task);//这个东西不知道在哪个地方使用

#endif /* CONFIG_CGROUP_SCHED */
 /*每个cpu都有一个rq,开始为所有cpu上的rq进行初始化*/
	for_each_possible_cpu(i) {
		struct rq *rq;

		rq = cpu_rq(i);/*使用per_cpu关联cpu与rq*/
		raw_spin_lock_init(&rq->lock);
		rq->nr_running = 0;/*系统初始化时,rq队列里面没有可运行的task,置为0*/
		rq->calc_load_active = 0;
		/*计算负载update period*/ 
		rq->calc_load_update = jiffies + LOAD_FREQ;//这个是什么呢?
		 /*初始化rq里面的cfs_rq,rt_rq和dl_rq
		 1. 初始化cfs_rq rb tree的root node和vruntime,这是主要的挑选哪个rb 
        node运行的关键,以后在分析,主要是一些cfs_rq成员变量的初始化*/
		init_cfs_rq(&rq->cfs);
		init_rt_rq(&rq->rt);
		init_dl_rq(&rq->dl);
#ifdef CONFIG_FAIR_GROUP_SCHED
		root_task_group.shares = ROOT_TASK_GROUP_LOAD;
		INIT_LIST_HEAD(&rq->leaf_cfs_rq_list);
        // 下面这两个是干什么?
/*
#ifdef CONFIG_FAIR_GROUP_SCHED
	/* list of leaf cfs_rq on this cpu: */
	struct list_head leaf_cfs_rq_list;
	struct list_head *tmp_alone_branch;
#endif /* CONFIG_FAIR_GROUP_SCHED */
*/
		rq->tmp_alone_branch = &rq->leaf_cfs_rq_list;
		/*
		 * How much cpu bandwidth does root_task_group get?
		 *
		 * In case of task-groups formed thr' the cgroup filesystem, it
		 * gets 100% of the cpu resources in the system. This overall
		 * system cpu resource is divided among the tasks of
		 * root_task_group and its child task-groups in a fair manner,
		 * based on each entity's (task or task-group's) weight
		 * (se->load.weight).
		 *
		 * In other words, if root_task_group has 10 tasks of weight
		 * 1024) and two child groups A0 and A1 (of weight 1024 each),
		 * then A0's share of the cpu resource is:
		 *
		 *	A0's bandwidth = 1024 / (10*1024 + 1024 + 1024) = 8.33%
		 *
		 * We achieve this by letting root_task_group's tasks sit
		 * directly in rq->cfs (i.e root_task_group->se[] = NULL).
		 */
		 /*初始化cfs task的带宽限制,比rt要复杂些,以后在展开讲解*/
		init_cfs_bandwidth(&root_task_group.cfs_bandwidth);
		/*上面分析task_group结构体的时候,我们知道一个进程组里面的进程可能处在不同
        的CPU上运行,那么也就是不同的cfs_rq和调度实体了,所以每个task都会标记是属于
         哪个cfs_rq和调度实体的。*/
		init_tg_cfs_entry(&root_task_group, &rq->cfs, NULL, i, NULL);
#endif /* CONFIG_FAIR_GROUP_SCHED */

 /*初始化rt可以运行的时间,默认值为950ms*/  
		rq->rt.rt_runtime = def_rt_bandwidth.rt_runtime;
#ifdef CONFIG_RT_GROUP_SCHED
/*初始化rt的rq和se*/
		init_tg_rt_entry(&root_task_group, &rq->rt, NULL, i, NULL);
#endif

//#define CPU_LOAD_IDX_MAX 5
		for (j = 0; j < CPU_LOAD_IDX_MAX; j++)
			rq->cpu_load[j] = 0;
/*标记load last update time*/
		rq->last_load_update_tick = jiffies;

#ifdef CONFIG_SMP
		rq->sd = NULL;
		rq->rd = NULL;
		/*初始化rq的capacity数值,来自dts,但是rq->cpu_capacity数值会随着系统变化
        ,这个有待解惑,目前我也没找到root cause*/
        //SCHED_CAPACITY_SCALE 是1024吗?
		rq->cpu_capacity = rq->cpu_capacity_orig = SCHED_CAPACITY_SCALE;
		/*下面三个与balance相关,active_balance是一个flag,目的在于实现负载的强制
        balance,而next_balance是一个周期性的tick,记录周期性balance的时间*/
		rq->balance_callback = NULL;
		rq->active_balance = 0;
		rq->next_balance = jiffies;
		rq->push_cpu = 0;
		rq->push_task = NULL;
		/*rq所处的cpu*/
		rq->cpu = i;
		rq->online = 0;
		/*rq idle,也就是cpu idle的时间戳*/  
		rq->idle_stamp = 0;
//const_debug unsigned int sysctl_sched_migration_cost = 500000UL;
		rq->avg_idle = 2*sysctl_sched_migration_cost;
		rq->max_idle_balance_cost = sysctl_sched_migration_cost;
#ifdef CONFIG_SCHED_WALT
/*在WALT中会计算处理每个irq的time,并转化为load*/ 
		rq->cur_irqload = 0;/*当前窗口的irq运行时间*/  
		rq->avg_irqload = 0;/*irq可能跨多个窗口运行,经过衰减算法,计算多
        的irq运行时间,作为avg_irqload个窗口*/
		rq->irqload_ts = 0;/*irq enter/exit时间戳*/ 
#endif
/*初始化cfs tasks的链表头*/
		INIT_LIST_HEAD(&rq->cfs_tasks);
/*将rq挂载在默认的root domain上,这个是什么意思呢???*/
// 将运行队列添加到默认调度域
		rq_attach_root(rq, &def_root_domain);

// 这个宏取值为Y
#ifdef CONFIG_NO_HZ_COMMON
		rq->nohz_flags = 0;
#endif

//这个宏没有设置
#ifdef CONFIG_NO_HZ_FULL
		rq->last_sched_tick = 0;
#endif
#endif
        // hrtick是什么东西呢????
		init_rq_hrtick(rq);
		/*设置rq iowait数值为0*/  
		atomic_set(&rq->nr_iowait, 0);

#ifdef CONFIG_INTEL_DWS
		init_intel_dws(rq);
#endif
	}
/*设置init_task load权重,每个task会根据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);

    // 这个是什么东西????
// arm64为空
	enter_lazy_tlb(&init_mm, current);

	/*
	 * During early bootup we pretend to be a normal task:
	 */
	  /*设置当前task为fair调度类,current也就是init_task thread*/  
	current->sched_class = &fair_sched_class;

	/*
	 * 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_sched_class*/  
    // 为什么设置成idle进程???
    // 将当前进程,即init_task设置为idle进程
	init_idle(current, smp_processor_id());
/*下次load update的时间*/ 
// 设置下载负载平衡的时间 // LOAD_FREQ等于5秒
	calc_load_update = jiffies + LOAD_FREQ;

#ifdef CONFIG_SMP
//static cpumask_var_t sched_domains_tmpmask; /* sched_domains_mutex */
	zalloc_cpumask_var(&sched_domains_tmpmask, GFP_NOWAIT);
	/* May be allocated at isolcpus cmdline parse time */

/* cpus with isolated domains */
//cpumask_var_t cpu_isolated_map;
	if (cpu_isolated_map == NULL)
		zalloc_cpumask_var(&cpu_isolated_map, GFP_NOWAIT);
	/*将boot cpu上当前task设置为idle_thread,对于其他丛cpu则在idle_threads_init里
     面,为每个cpu fork出idle_threads*/
	idle_thread_set_boot_cpu();//那和上面函数的区别是哪里呢???
	/*设置rq age_stamp,即rq的启动时间(包括idle和running时间),不是运行时间,*/
	set_cpu_rq_start_time();
#endif
	init_sched_fair_class();

// 这个宏是没有定义的
#ifdef CONFIG_64BIT_ONLY_CPU
	arch_get_64bit_only_cpus(&b64_only_cpu_mask);
#ifdef CONFIG_SCHED_COMPAT_LIMIT
	/* get cpus that support AArch32 and store in compat_32bit_cpu_mask */
	cpumask_andnot(&compat_32bit_cpu_mask, cpu_present_mask,
		&b64_only_cpu_mask);
#endif
#endif
 /*调度器开始工作了*/
	scheduler_running = 1;
}
/*
上面的调度器初始化工作完毕,后面就开始正常的调度工作了.如下 
1. scheduler_tick周期性的TICK_NSEC 
2. 根据进程状态的变化进行调度(比如进程的创建,进程从idle被wakeup等)
*/

​

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值