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.2 核心概念

2.2.4 核心概念 - 运行队列

简要介绍runqueue

调度器的核心工作就是把可运行的任务扔到CPU上去运行。如何有效、合理地组织可运行的任务,是调度器需要考虑的重要问题。

Linux 内核使用一个运行队列(runqueue)来存放可运行的任务,该数据结构的定义如下:

/* file: kernel/sched/sched.h */
struct rq {
    unsigned int nr_running;

    u64 nr_switches;

    /*
     * runqueue 采用的模块化的实现方式,各个不同的 Scheduling Class 都有自己的
     runqueue 实现。不同的 runqueue 的数据结构保存在如下属性之中
    */
    struct cfs_rq cfs; /* CFS runqueue 的实现 */
    struct rt_rq rt;   /* RT runqueue 的实现 */
    struct dl_rq dl;   /* Deadline runqueue 的实现 */

    struct task_struct __rcu *curr; /* 当前 runqueue 中正在运行的进程 */
    struct task_struct *idle;       /* Idle 调度类的任务 */
    struct task_struct *stop;       /* Stop 调度类的任务 */
    struct mm_struct *prev_mm;

    atomic_t nr_iowait;

#ifdef CONFIG_SMP
    struct root_domain *rd;
    struct sched_domain __rcu *sd;

    unsigned long cpu_capacity;
    unsigned long cpu_capacity_orig;
#endif /* CONFIG_SMP */

#ifdef CONFIG_SCHEDSTATS
    /* latency stats */
    struct sched_info rq_sched_info;
    unsigned long long rq_cpu_time;
    /* could above be rq->cfs_rq.exec_clock + rq->rt_rq.rt_runtime ? */

    /* sys_sched_yield() stats */
    unsigned int yld_count;

    /* schedule() stats */
    unsigned int sched_count;
    unsigned int sched_goidle;
#endif
};

此处我们删除了该结构体的大多数字段,仅仅保留了一些主要的字段一探究竟,这些字段涉及如下几个方面:

  1. 具体调度类的运行队列。不同的调度类选择下一个任务的逻辑是不同的,因此不同的调度类会有不同的调度队列实现方式,例如字段 struct cfs_rq cfs 就是 CFS 的调度队列

  2. 当前正在运行的进程,以及 stop 与 idle 这种特殊任务

  3. 如果是 SMP 架构,则 rq 中还会包含调度域的字段,调度器使用调度域中的信息来做负载均衡

  4. 一些统计信息

rq 是系统可运行任务的容器,调度器的很多工作都是围绕着 rq 来进行的,调度类 struct sched_class 所申明的函数中,绝大多数函数都与 rq 相关。在系统中,每个 CPU 都有一个自己的 rq, 这样可以避免多个 CPU 访问同一个 rq 时产生的并发问题,提升调度器效率。

在 Linux 的早期实现中,系统只维护一个全局的 rq,为了解决同步问题,调度器通过自旋锁来保护对队列的同步访问。2.4 版本的 O(n) 调度器采用的就是这种实现方式。

对于有些变量,内核会为每个 CPU 单独维护一份拷贝,这种变量叫着 per-cpu 变量(per-cpu variable),除了 rq, 还有很多其他的场景需要这种变量。内核定义了各种宏和工具函数来对这类变量进行处理,所有的定义都在文件 include/linux/percpu-defs.h 中。例如申明 per-cpu 变量的宏定义如下:

#define DEFINE_PER_CPU(type, name)              \
    DEFINE_PER_CPU_SECTION(type, name, "")
Previous2.2.3 核心概念 - 调度策略Next2.2.5 核心概念 - 优先级

Last updated 3 years ago

Was this helpful?