日期 内核版本 CPU架构 作者
2019.04.06 Linux-5.0 PowerPC LoneHugo

系列文章:https://blog.csdn.net/Vince_/article/details/89054330

1. 调度初始化涉及的内容

读者可以看到我们一直在强调调度与Linux中各个模块关联,所以在讲解调度初始化的过程中,我们会将与调度相关的子系统的初始化一并讲到,以便我们对整个概念有宏观的认识。

调度是由时钟中断和定时器驱动的,因此初始化过程讲到了时钟中断初始化的内容,包括硬件中断和软中断后半部的初始化。

2. 调度初始化入口sched_init

源码:https://elixir.bootlin.com/linux/v5.0.7/source/kernel/sched/core.c#L5927

start_kernel函数调用sched_init进入调度的初始化。首先分配alloc_size大小的内存,初始化root_task_group,root_task_group为系统默认的task group,系统启动阶段每个进程都属于该task group需要注意root_task_group中的成员是针对perCPU的。初始化完成之后将init_task标记为idle进程。具体看下面函数中的注释。

void __init sched_init(void)
{int i, j;unsigned long alloc_size = 0, ptr;/* calculate the size to be allocated for root_task_group items.* some items in the struct task_group are per-cpu fields, so use * no_cpu_ids here.*/
#ifdef CONFIG_FAIR_GROUP_SCHEDalloc_size += 2 * nr_cpu_ids * sizeof(void **);
#endif
#ifdef CONFIG_RT_GROUP_SCHEDalloc_size += 2 * nr_cpu_ids * sizeof(void **);
#endifif (alloc_size) {/* allocate mem here. */ptr = (unsigned long)kzalloc(alloc_size, GFP_NOWAIT);#ifdef CONFIG_FAIR_GROUP_SCHEDroot_task_group.se = (struct sched_entity **)ptr;ptr += nr_cpu_ids * sizeof(void **);root_task_group.cfs_rq = (struct cfs_rq **)ptr;ptr += nr_cpu_ids * sizeof(void **);#endif /* CONFIG_FAIR_GROUP_SCHED */
#ifdef CONFIG_RT_GROUP_SCHEDroot_task_group.rt_se = (struct sched_rt_entity **)ptr;ptr += nr_cpu_ids * sizeof(void **);root_task_group.rt_rq = (struct rt_rq **)ptr;ptr += nr_cpu_ids * sizeof(void **);#endif /* CONFIG_RT_GROUP_SCHED */}
#ifdef CONFIG_CPUMASK_OFFSTACK/* Use dynamic allocation for cpumask_var_t, instead of putting them on the stack. * This is a bit more expensive, but avoids stack overflow. * Allocate load_balance_mask for every cpu below.*/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 *//* init the real-time task group cpu time percentage. * the hrtimer of def_rt_bandwidth is initialized here.*/init_rt_bandwidth(&def_rt_bandwidth,global_rt_period(), global_rt_runtime());/* init the deadline task group cpu time percentage. */init_dl_bandwidth(&def_dl_bandwidth,global_rt_period(), global_rt_runtime());#ifdef CONFIG_SMP/* 初始化默认调度域,调度域包含一个或者多个CPU,负载均衡是在调度域之内执行,相互之间进行隔离 */init_defrootdomain();
#endif#ifdef CONFIG_RT_GROUP_SCHEDinit_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加入到錿ask_groups全局链表 */list_add(&root_task_group.list, &task_groups);INIT_LIST_HEAD(&root_task_group.children);INIT_LIST_HEAD(&root_task_group.siblings);/* 初始化自动分组 */autogroup_init(&init_task);#endif /* CONFIG_CGROUP_SCHED *//* 遍历每个cpu的运行队列,对其进行初始化 */for_each_possible_cpu(i) {struct rq *rq;rq = cpu_rq(i);raw_spin_lock_init(&rq->lock);/* CPU运行队列的所有调度实体(sched_entity)的数目 */rq->nr_running = 0;/* CPU负载 */rq->calc_load_active = 0;/* 负载更新时间 */rq->calc_load_update = jiffies + LOAD_FREQ;/* 分别初始化运行队列的cfs rt和dl队列 */init_cfs_rq(&rq->cfs);init_rt_rq(&rq->rt);init_dl_rq(&rq->dl);
#ifdef CONFIG_FAIR_GROUP_SCHED/* root的CPU总的配额 */root_task_group.shares = ROOT_TASK_GROUP_LOAD;INIT_LIST_HEAD(&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_bandwidth,普通进程占有的CPU资源,初始化调度类相应的高精度定时器 */init_cfs_bandwidth(&root_task_group.cfs_bandwidth);/* 当前CPU运行队列的cfs_rq的task_group指定为tg, 即root_task_group *//* 指定cfs_rq的rq为当前CPU运行队列rq *//* root_task_group在当前CPU上的cfs_rq *//* 目前schedule_entity se是空 */init_tg_cfs_entry(&root_task_group, &rq->cfs, NULL, i, NULL);
#endif /* CONFIG_FAIR_GROUP_SCHED */rq->rt.rt_runtime = def_rt_bandwidth.rt_runtime;
#ifdef CONFIG_RT_GROUP_SCHED/* 类似前面init_tg_cfs_entry的初始化, 完成相互赋值 */init_tg_rt_entry(&root_task_group, &rq->rt, NULL, i, NULL);
#endif/* 初始化该队列所保存的每个CPU的负载情况 */for (j = 0; j < CPU_LOAD_IDX_MAX; j++)rq->cpu_load[j] = 0;/* 该队列最后更新CPU负载的时间 */rq->last_load_update_tick = jiffies;#ifdef CONFIG_SMP/* 初始化负载均衡相关的参数 */rq->sd = NULL;rq->rd = NULL;rq->cpu_capacity = rq->cpu_capacity_orig = SCHED_CAPACITY_SCALE;rq->balance_callback = NULL;rq->active_balance = 0;rq->next_balance = jiffies;rq->push_cpu = 0;rq->cpu = i;rq->online = 0;rq->idle_stamp = 0;rq->avg_idle = 2*sysctl_sched_migration_cost;rq->max_idle_balance_cost = sysctl_sched_migration_cost;INIT_LIST_HEAD(&rq->cfs_tasks);/* CPU运行队列加入到默认调度域中 */rq_attach_root(rq, &def_root_domain);
#ifdef CONFIG_NO_HZ_COMMON/* 动态时钟使用标志位,初始时间未使用 */rq->nohz_flags = 0;
#endif
#ifdef CONFIG_NO_HZ_FULL/* 动态时钟使用的标志位,用于保存上次调度tick发生时间 */rq->last_sched_tick = 0;
#endif
#endif/* 运行队列高精度定时器的初始化,还未正式生效 */init_rq_hrtick(rq);atomic_set(&rq->nr_iowait, 0);}/* 设置初始化进程的load权重 */set_load_weight(&init_task);#ifdef CONFIG_PREEMPT_NOTIFIERS/* init_task的抢占通知链初始化 */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);/** During early bootup we pretend to be a normal task:*//* 设定初始化进程采用fair调度类 */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调度器 */init_idle(current, smp_processor_id());calc_load_update = jiffies + LOAD_FREQ;#ifdef CONFIG_SMPzalloc_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/* 初始化fair调度类,其实实际上是注册SCHED_SOFTIRQ类型的软中断处理函数run_rebalance_domains,执行负载平衡过程 *//* 这里的问题是SCHED_SOFTIRQ软中断是何时触发?*/init_sched_fair_class();/* 标记调度器开始运行,但是此时系统只有init_task一个进程,且为idle进程,* 定时器暂时还未启动,不会调度到其它进程,所以继续回到start_kernel执行初始化过程。*/scheduler_running = 1;
}

3. 时钟中断相关的初始化

在sched_init初始化之后,继续回到start_kernel执行,跟调度相关的内容是:

3.1 init_IRQ

该函数中会初始化IRQ的栈空间,包括系统中所有的软件中断和硬件中断。时钟中断是调度的驱动因素,包括硬件中断和软中断下半部,在这里也进行了初始化。中断相关的内容后面章节会有详细的介绍,此处需要了解整个初始化流程,知道这个点做了什么。

3.2 init_timers

此处会初始化timer,注册TIMER_SOFTIRQ软中断回调函数run_timer_softirq,关于softirq的内容我会在最后进行介绍。既然在这里注册了softirq,那么在哪里开始激活或启动该softirq呢?该softirq的作用是什么?

在时钟中断的注册章节我们会看到,tick_handle_periodic为时钟中断的事件回调函数,在time_init中被赋值到时钟中断的回调函数钩子处,发生时钟中断是会被调用做中断处理。该函数最终调用tick_periodic,继续调用update_process_times,进而再调用run_local_timers函数来打开TIMER_SOFTIRQ,同时run_local_timers也调用接口hrtimer_run_queues运行高精度定时器。这是中断处理的典型方式,即硬件中断处理关键部分,启动softirq后打开硬件中断响应,更多的事务在软中断下半部中处理。关于该软中断的具体作用后面会详细介绍,这里需要了解的是它会激活所有过期的定时器。

3.3 time_init

执行时钟相关的初始化,后面会看到,我们在系统初始化初期的汇编阶段会注册硬件中断向量表,但是中断设备和事件处理函数并未初始化,这里调用init_decrementer_clockevent初始化时钟中断设备,并初始化时间回调tick_handle_periodic;同时调用tick_setup_hrtimer_broadcast注册高精度定时器设备及其回调,在中断发生时实际会被执行。此时硬件中断被激活。

3.4 sched_clock_postinit和sched_clock_init

开启调度时间相关的定时器定期更新信息。

深入解读Linux进程调度系列(1)——调度的初始化相关推荐

  1. Linux进程管理与调度-之-目录导航【转】

    转自:http://blog.csdn.net/gatieme/article/details/51456569 版权声明:本文为博主原创文章 && 转载请著名出处 @ http:// ...

  2. (6)Linux进程调度-实时调度器

    目录 背景 1. 概述 2. 数据结构 3. 流程分析 3.1 运行时统计数据 3.2 组调度 3.3 带宽控制 3.4 调度器函数分析 3.4.1 pick_next_task_rt 3.4.2 e ...

  3. Linux进程调度 - 实时调度器 LoyenWang

    背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...

  4. linux 2.6内核进程调度,Linux2.6内核进程调度系列--scheduler_tick()函数2.更新实时进程的时间片,...

    Linux2.6内核进程调度系列--scheduler_tick()函数2.更新实时进程的时间片, RT /** * 递减当前进程的时间片计数器,并检查是否已经用完时间片. * 由于进程的调度类型不同 ...

  5. Linux进程调度:完全公平调度器 Completely Fair Scheduler 内幕| linux-2.6

    https://www.ibm.com/developerworks/cn/linux/l-completely-fair-scheduler/index.html? 目录 Linux 调度器简史 C ...

  6. (5)Linux进程调度-CFS调度器

    目录 背景 1. 概述 2. 数据结构 2.1 调度类 2.2 rq/cfs_rq/task_struct/task_group/sched_entity 3. 流程分析 3.1 runtime与vr ...

  7. Linux进程调度器概述--Linux进程的管理与调度(十五)

    日期 内核版本 架构 作者 GitHub CSDN 2016-06-14 Linux-4.6 X86 & arm gatieme LinuxDeviceDrivers Linux进程管理与调度 ...

  8. Linux进程调度 - CFS调度器 LoyenWang

    背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...

  9. Linux进程调度-deadline调度器

    Linux内核中定义了5个调度器类,分别对应5个调度器,调度优先级顺序由高到低依次为:stop_sched_class.dl_sched_class.rt_sched_class.fair_sched ...

  10. linux进程调度之 FIFO 和 RR 调度策略

    转载 http://blog.chinaunix.net/uid-24774106-id-3379478.html  linux进程调度之 FIFO 和 RR 调度策略 2012-10-19 18:1 ...

最新文章

  1. eNSP模拟器路由器无法正常启动一直显示“#”——问题解决方法
  2. NYOJ 469 擅长排列的小明 II (dp问题)
  3. 正则表达式学习 (一) 转
  4. Linux 系统备份恢复工具 SYSTEM-RESCUE-CD 的使用
  5. Javascript开发技巧(JS中的变量、运算符、分支结构、循环结构)
  6. 一种使用SOC精确控制脉冲的方法
  7. springboot设置局域网访问
  8. 优动漫PAINT的界面如何适配数位屏/平板?
  9. web打印模板神器reportbro—汉化及中文字体配置(上)
  10. 史上最全的Nokia3250参数
  11. Tensorflow(八) —— Tensor的合并与切割
  12. 项目管理--关键链方法
  13. Linux下QT平台Mysql数据库开发环境配置
  14. Java小白自学8:循环结构练习题(一)
  15. python中获取文件扩展名
  16. Chrome 便捷设置
  17. 警惕,保护自己的隐私,Ucloo优库网已经明显是犯法行为了!
  18. JS 输出指定格式的时间
  19. 因为你的错过,转身为陌路
  20. jplayer实现单曲循环

热门文章

  1. 你真的了解 MySQL 数据库的运行状况吗?
  2. JDK常见问题 环境变量配置
  3. 简单的三层框架以及使用dbutils进行数据库操作(入门)
  4. anaconda 卸载_Windows安装Anaconda使用教程
  5. 蓝桥杯第五届JavaC组杨辉三角问题解决方法
  6. Windows下SVN备份脚本
  7. Java基础-运行原理及变量(01)
  8. 智能美观网速快 有这样的无线路由吗?
  9. 六、openstack安装之Horizon篇
  10. log4j.properties中log4j.rootLogger 与log4j.rootCategory 有什么区别 .