Linux核心概念详解
  • 0. Linux核心概念详解
  • 1. 调试环境
  • 2. Linux 调度器
    • 2.1 任务
    • 2.2 核心概念
      • 2.2.1 核心概念 - 调度实体
      • 2.2.2 核心概念 - 调度类
      • 2.2.3 核心概念 - 调度策略
      • 2.2.4 核心概念 - 运行队列
      • 2.2.5 核心概念 - 优先级
    • 2.3 演进历史
      • 2.3.1 O(n)调度器 - 调度逻辑
      • 2.3.2 O(n)调度器 - 时间分配
      • 2.3.3 O(n)调度器 - 调度时机
      • 2.3.4 O(1)调度器 - 简介
      • 2.3.5 O(1)调度器 - 调度逻辑
      • 2.3.6 O(1)调度器 - 时间分配
      • 2.3.7 RSDL
      • 2.3.8 CFS
    • 2.4 DL调度器
      • 2.4.1 DL调度器 - 调度算法
      • 2.4.2 DL调度器 -核心代码
    • 2.5 RT调度器
    • 2.6 CFS
      • 2.6.1 公平性
      • 2.6.2 调度逻辑
      • 2.6.2.1 调度逻辑 - 数据结构
      • 2.6.2.2 调度逻辑 - vruntime
      • 2.6.2.3 调度逻辑 - 调度周期
      • 2.6.2.4 调度逻辑 - 调度节拍
      • 2.6.2.5 调度逻辑 - 任务抢占
      • 2.6.2.6 调度逻辑 - 调度时机
      • 2.6.3 组调度
      • 2.6.3.1 组调度 - 数据结构
      • 2.6.3.2 组调度 - 调度逻辑
      • 2.6.3.3 组调度 - 时间分配
      • 2.6.3.4 组调度 - 任务组权重
      • 2.6.4 带宽控制
      • 2.6.4.1 带宽控制 - 数据结构
      • 2.6.4.2 带宽控制 - 带宽时间
      • 2.6.4.3 带宽控制 - 挂起与解挂
      • 2.6.4.3 带宽控制 - 定时器
    • 2.7 负载追踪
      • 2.7.1 负载追踪 - 简介
      • 2.7.2 负载追踪 - 数据结构
      • 2.7.3 负载追踪 - 计算负载
      • 2.7.4 负载追踪 - 更新负载
    • 2.8 负载均衡
      • 2.8.1 简介
      • 2.8.2 CPU的拓扑结构
      • 2.8.3 数据结构
      • 2.8.4 算法思路
      • 2.8.5 触发时机
      • 2.8.6 总结
  • 3. LINUX 内存管理
    • 3.1 寻址模式
      • 3.1.1 地址
      • 3.1.2 地址转换
      • 3.1.3 Linux的地址空间
    • 3.2 物理内存
      • 3.2.1 数据结构
      • 3.2.2 初始化
      • 3.2.3 物理内存模型
      • 3.2.4 Buddy System(伙伴系统)
      • 3.2.5 SLAB/SLUB/SLOB
Powered by GitBook
On this page

Was this helpful?

  1. 2. Linux 调度器
  2. 2.6 CFS

2.6.2.6 调度逻辑 - 调度时机

从调度器的角度来看,真正的调度(即调度器完成上下文切换,正儿八经地换一个任务来执行)仅发生在函数 schedule() 中,剔除额外代码,我们来看一下该函数的主要流程:

/* file: kernel/sched/core.c */
asmlinkage __visible void __sched schedule(void) {
struct task_struct *tsk = current;

do {
preempt_disable();
/* 调用函数 __schedule 来做具体的工作 */
__schedule(false);
sched_preempt_enable_no_resched();
/* need_resched用来判断当前任务是否应该被抢占,此时的当前任务就是函数__schedule最新选择的任务,如果是的话那么继续调用函数__schedule以便调用下一个合适的任务。*/
} while (need_resched());
}

static void __sched notrace __schedule(bool preempt) {
struct task_struct *prev, *next;
unsigned long *switch_count;
unsigned long prev_state;
struct rq_flags rf;
struct rq *rq;
int cpu;

/* 获取到当前CPU序号,进而获取到其runqueue */
cpu = smp_processor_id();
rq = cpu_rq(cpu);
/* rq->curr 是当前正在执行的任务 */
prev = rq->curr;

prev_state = prev->state;
if (!preempt && prev_state) {
if (signal_pending_state(prev_state, prev)) {
    prev->state = TASK_RUNNING;
} else {
    /* preempt 如果为false,
        则说明此次调度不是由于任务抢占导致的,那么导致调度发生的原因就是任务主动要求让出CPU,
        对于由于IO事件进入睡眠的任务而言,需要先将其从运行队列中踢出去。该函数最终会调用调度类(sched_class)的
        dequeue_task 方法完成具体工作。*/
    deactivate_task(rq, prev, DEQUEUE_SLEEP | DEQUEUE_NOCLOCK);
}
}

/* 从队列中选择下一个任务,该函数最终会调用调度类(sched_class的函数pick_next_task方法。对于CFS而言,就是选择vruntime最小的任务
*/
next = pick_next_task(rq, prev, &rf);
/* 清除 prev 任务的TIF_NEED_RESCHED标记,因为此时它已经被抢占了 */
clear_tsk_need_resched(prev);

if (likely(prev != next)) {
/* 完成上下文切换,CPU将开始执行刚刚挑出来的任务next了 */
rq = context_switch(rq, prev, next, &rf);
}
}

关于选择下一个任务的语句 next = pick_next_task(rq, prev, &rf);, 其中函数 pick_next_task() 就是我们在调度类中讨论的那个方法,CFS 的实现是函数 pick_next_task_fair, 他的主要逻辑就是从红黑树中选择最左侧的任务,我们在这里不再深入细节讨论。

函数 schedule() 最后调用 context_switch() 完成上下文切换,至此,整个调度工作就完成了!

Previous2.6.2.5 调度逻辑 - 任务抢占Next2.6.3 组调度

Last updated 3 years ago

Was this helpful?