Linux内核架构读书笔记 - 2.5.4 核心调度器

什么是核心调度器?

  参考前面的博文http://www.cnblogs.com/songbingyu/p/3696414.html

  1 周期性调度器

    作用:

    • 管理内核中与整个系统和各个进程的调度相关的统计量

    • 负责当前调度类的周期性调度方法

    kernel/sched.c


 1 /*
2 * This function gets called by the timer code, with HZ frequency.
3 * We call it with interrupts disabled.
4 *
5 * It also gets called by the fork code, when changing the parent‘s
6 * timeslices.
7 */
8 void scheduler_tick(void)
9 {
10 int cpu = smp_processor_id();
11 struct rq *rq = cpu_rq(cpu);
12 struct task_struct *curr = rq->curr;
13 u64 next_tick = rq->tick_timestamp + TICK_NSEC;
14
15 spin_lock(&rq->lock);
16 __update_rq_clock(rq);
17 /*
18 * Let rq->clock advance by at least TICK_NSEC:
19 */
20 if (unlikely(rq->clock < next_tick))
21 rq->clock = next_tick;
22 rq->tick_timestamp = rq->clock;
23 update_cpu_load(rq);
24 ....

    _update_rq_clock 处理就绪队列时钟更新,本质上增加struct rq 的时间戳

    update_cpu_load 负责更新就绪队列的cpu_load[]
数组,本质上相当与将数组中先前存储的负载值向后移动一个位置,将当前就绪队列的负载值计入数组的第一个位置、、、

    kernel/sched.c

1     if (curr != rq->idle) /* FIXME: needed? */
2 curr->sched_class->task_tick(rq, curr);

    task_tick 实现取决与底层的调度器类

    注意:
如果进程应该被重新调度,调度器类会在task_struct中设置TIF_NEED_RESCHED标志,已表示该请求,内核会载接下来的适当时机完成该请求

  2 主调度器

   在内核的许多地方,如果要将CPU 分配给当前活动进程不同的另一个进程,都会直接调用主调度器函数(schedule)

  kernel/sched.c


 1 /*
2 * schedule() is the main scheduler function.
3 */
4 asmlinkage void __sched schedule(void)
5 {
6 struct task_struct *prev, *next;
7 long *switch_count;
8 struct rq *rq;
9 int cpu;
10
11 need_resched:
12 preempt_disable();
13 cpu = smp_processor_id();
14 rq = cpu_rq(cpu);
15 rcu_qsctr_inc(cpu);
16 prev = rq->curr;
17 switch_count = &prev->nivcsw;

  

类似与周期性调度器,内核也利用该时机来更新就绪队列的时钟,并清楚当前运行进程中的重调度标志TIF_NEED_RESCHED

  kernel/sched.c

1 __update_rq_clock(rq);
2 spin_lock(&rq->lock);
3 clear_tsk_need_resched(prev);

  如果当前进程原来处于可中断睡眠状态但现在接受到信号,,那吗它必须再次提升为运行进程,负责相应调度器类的方法使进程停止活动(deactivate_task实质上最终调用sched_class->dequeue_task)


1 if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) {
2 if (unlikely((prev->state & TASK_INTERRUPTIBLE) &&
3 unlikely(signal_pending(prev)))) {
4 prev->state = TASK_RUNNING;
5 } else {
6 deactivate_task(rq, prev, 1);
7 }
8 switch_count = &prev->nvcsw;
9 }

  

  put_prev_task同志调度器类当前进程将要被另一个进程代替,不是从就绪队列移除,而是提供一个时机进行一些统计工作

  pick_next_task 选择下一个进程

1     prev->sched_class->put_prev_task(rq, prev);
2 next = pick_next_task(rq, prev);

  不见得必然选择一个进程,有可能其他进程都在睡眠

  kernel/sched.c

1 if (likely(prev != next)) {
2 rq->nr_switches++;
3 rq->curr = next;
4 ++*switch_count;
5
6 context_switch(rq, prev, next); /* unlocks the rq */

  content_switch 一个接口,供访问特定与体系结构的方法

  

  下面代码检测当前的重调度位是否设置,并跳转

1     if (unlikely(test_thread_flag(TIF_NEED_RESCHED)))
2 goto need_resched;

  注意:上述代码可能载两个上下文中执行  无上下文切换,schedule 末尾直接执行,如果执行上下文切换,在新的上下文执行,所以需要current 和
test_thread_flag 找到当前进程

  3 与fork 交互

  使用fork或其变体建立新进程时候,调度器有机会用sched_fork 挂钩到该进程

  再单处理器上,执行三个操作,初始化新进程与调度相关字段,建立数据结构 确定进程的动态优先级

  kernel/sched.c/sched_fork

    /*
* Make sure we do not leak PI boosting priority to the child:
*/
p->prio = current->normal_prio;
if (!rt_prio(p->prio))
p->sched_class = &fair_sched_class;

   在使用wake_up_new_task 唤醒新的进程,是调度器与创建逻辑交互的第二时机,内核会调用task_new
函数,将进程加入到相应的就绪队列

  

  4 上下文切换

  content_switch

  kernel/sched.c


 1 /*
2 * context_switch - switch to the new MM and the new
3 * thread‘s register state.
4 */
5 static inline void
6 context_switch(struct rq *rq, struct task_struct *prev,
7 struct task_struct *next)
8 {
9 struct mm_struct *mm, *oldmm;
10
11 prepare_task_switch(rq, prev, next);
12 mm = next->mm;
13 oldmm = prev->active_mm;

  上下文切换调用两个特定与体系结构的的函数

    1 switch_mm

    2 switch_to

  kernel/sched.c

1     if (unlikely(!mm)) {
2 next->active_mm = oldmm;
3 atomic_inc(&oldmm->mm_count);
4 enter_lazy_tlb(oldmm, next);
5 } else
6 switch_mm(oldmm, mm, next);

  entry_lazy_tlb 通知底层结构体系不许要切换虚拟地址空间的用户部分

  

  如果前一进程是内核进程(prev-》mm 为 null ) ,其 active_mm 必须重置为null

  kernel/sched.c

1     if (unlikely(!prev->mm)) {
2 prev->active_mm = NULL;
3 rq->prev_mm = oldmm;
4 }

  

  最后用switch_to 完成进程切换

  switch_to 之后的代码只有当前进程下一次被选择运行是才会运行

  finish_task_switch 完成一些清理工作


 1     /* Here we just switch the register state and the stack. */
2 switch_to(prev, next, prev);
3
4 barrier();
5 /*
6 * this_rq must be evaluated again because prev may have moved
7 * CPUs since it called schedule(), thus the ‘rq‘ on its stack
8 * frame will be invalid.
9 */
10 finish_task_switch(this_rq(), prev);

  

  •   switch_to 的复杂之处

  finish_task_switch 特点,调度器可能选择了一个新的进程,但是清理则是针对此前的活动进程

  eg

  

  其实上面只是表达一个意思

  

  在新的进程被再次执行时候,获得上一次运行的是那一个进程

  还没想明白。。。mark

  •   惰性FPU 模式

  浮点寄存器,除非有程序使用,负责不会保存,此外除非有应用程序需要,负责这些寄存器不会恢复

  

Linux内核架构读书笔记 - 2.5.4 核心调度器,码迷,mamicode.com

时间: 2024-12-17 10:43:51

Linux内核架构读书笔记 - 2.5.4 核心调度器的相关文章

Linux内核架构读书笔记 - 2.5.3 处理优先级

1 优先级的内核表示 内核使用 0 - 139 表示内部优先级,值越低,优先级越高.0 -99 实时进程使用 nice 值 [-20,19]映射到范围100 - 139,如下图 内核定义了一系列宏来辅助优先级之间的转换 sched.h 1 /* 2 * Priority of a process goes from 0..MAX_PRIO-1, valid RT 3 * priority is 0..MAX_RT_PRIO-1, and SCHED_NORMAL/SCHED_BATCH 4 *

Linux内核架构读书笔记 - 2.5.2 数据结构

调度系统各个组建关系如下 激活调度器两种方法:进程睡眠或其他原因放弃CPU,周期性检测 上述两个组件统称为通用调度器或核心调度器. 调度器用于判断接下来运行那个进程,内核支持不同的调度策略( 完全公平调度 实时调度 无事可做的空闲调度进程) 调度器被调用时候 需要执行体系相关的进程上下文切换 每个进程属于某个调度器类,各个调度器负责管理所属进程,通用调度器不涉及进程管理,都由调度器来 下面分别讲述: task_struct 成员 sched.h 1 struct task_struct { 2

20135239 益西拉姆 linux内核分析 读书笔记之第四章

chapter 4 进程调度 4.1 多任务 多任务操作系统就是能同时并发的交互执行多个进程的操作系统. 多任务系统可以划分为两类: - 非抢占式多任务: - 进程会一直执行直到自己主动停止运行(这一步骤称为让步) - 抢占式多任务: - Linux/Unix使用的是抢占式的方式:强制的挂起进程的动作就叫做抢占.进程在被抢占之前能够运行的时间是预先设置好的(也就是进程的时间片) 4.2 linux的进程调度 O(1)调度器:对大服务器的工作负载很理想,但是缺少交互进程. 反转楼梯最后期限调度算法

《深入Linux内核架构》笔记 --- 第一章 简介和概述

Linux将虚拟地址空间划分为两个部分,分别称为内核空间和用户空间 各个系统进程的用户空间是完全彼此分离的,而虚拟地址空间顶部的内核空间总是同样的,无论当前执行的是哪个进程. 尽管Intel处理器区分4中特权级别,当Linux只使用两种不同的状态:核心态和用户态.两种状态的关键差别在于对高于TASK_SIZE的内存区域的访问. 在中断上下文中运行不能访问虚拟地址空间中的用户空间部分.CPU大多数时间都在执行用户空间的代码,当应用程序执行系统调用时,则切换到核心态,内核将完成其请求.在此期间,内核

大型网站技术架构 读书笔记2 大型网站核心架构要素

通常情况下,一个网站的架构出来功能性需求外,还应该考量以下五个方面: 性能 可用性 伸缩性 扩展性 安全性 性能 性能的官方解释,我就不说了.对用户来说,就是系统的反应速度是否快. 对网站来说,性能问题是无处不在的,继而,我们优化性能的手段也有很多. 我们从前到后一个一个来说 在浏览器端,可以通过浏览器缓存,页面压缩,合理布局页面等方式 还可以使用cdn,让一些静态文件放在网络服务商的机房,这样离用户近一些. 也可以使用反向代理,把静态文件存在反向代理服务器上,例如apache 服务器端就是缓存

《深入Linux内核架构》附录A&lt;体系结构相关知识&gt;笔记

A.1 概述 为便于扩展到新的体系结构,内核严格隔离了体系结构相关和体系结构无关的代码.内核中特定于处理器的部分,包含定义和原型的头文件保存在include/asm-arch/(例如,include/asm-arm/)目录下,而C语言和汇编程序源代码实现则保存在arch/arch/(例如,arch/arm/)目录下. 联编系统也考虑到一般代码可能需要借助于特定于体系结构的机制.所有特定于处理器的头文件都位于include/asm-arch/.在内核配置为特定的体系结构之后,则建立符号链接incl

《Linux程序设计》&mdash;&mdash;读书笔记(2)

Linux环境: 无论操作系统何时启动一个新程序,参数argc和argv都会被设置并传递给main(即使main函数未声明参数,此时只是不能使用这些参数).这些参数通常由另一个程序提供,一般是shell,它要求操作系统启动该新程序.shell会接受用户输入的命令行,将命令行分解成单词,然后把这些单词放入argv数组. 命令行参数在向程序传递信息方面很有用,很多工具程序使用命令行参数来改变程序的行为或设置选项(这一点我们很熟悉,但是可能很少会联想到正是argc.argv参数在这里发挥作用). 尽管

《Linux程序设计》&mdash;&mdash;读书笔记

UNIX是一套计算机操作系统应遵循的特定规范(定义了所有必需的操作系统函数的名称.接口和行为),完全符合该规范的操作系统才允许使用"UNIX"的商标,否则就是"类UNIX操作系统". 许多类UNIX系统都是商业性质的,如IBM的AIX,HP的HP-UX和Sun的Solaris:也有一些免费的,如FreeBSD和Linux. Linux是一个可以自由发布的类UNIX内核实现.发展之路:UNIX->Minix->Linux. Linux程序: Linux应用

搭建《深入Linux内核架构》的Linux环境

作者 彭东林 [email protected] 软件 Host: Ubuntu14.04 64 Qemu 2.8.0 Linux 2.6.24 busybox 1.24.2 gcc 4.4.7 概述 为了尽量还原<深入Linux内核架构>这本书的环境,我下载了Linux 2.6.24,由于这个内核版本比较老,所以用最新的gcc编译会有问题,所以需要安装一个比较老的gcc,从该内核的README得知,gcc的版本最少应该是3.2. 正文 一.安装GCC 使用apt-cache search g