核心调度器管理活动进程的主要数据结构是就绪队列rq, 每个cpu都有自己的就绪队列,而每个活动进程某一时刻只会在一个就绪队列中。
对于调度器而言,由于支持多种调度算法,所以各种调度算法的就绪队列会嵌入到就绪队列的子队列中。
就绪队列具体定义见rq定义
成员名 | 类型 | 说明 | 备注 |
---|
raw_spinlock_t lock;
访问就绪队列所使用的锁 , 可以使用函数raw_spin_lock_irq()获取锁后关闭中断,或者使用函数 raw_spin_unlock_irq()释放锁并开启中断
unsigned int nr_running ;
当前就绪队列可投入运行的调度实体个数,如进程、线程等调度实体,但当前正在运行 的调度实体不计入 nr_running 中,因为它已经从就绪队列中脱离. 函数nr_running()获取所有,同时文件文件 proc/loadavg也可了解相关信息
#define CPU_LOAD_IDX_MAX 5
unsigned long cpu_load[CPU_LOAD_IDX_MAX];
用于跟踪 CPU 负载|大小为CPU_LOAD_IDX_MAX,值为5,也可通过文件proc/sched_debug了解负载信息.
unsigned long last_load_update_tick;
更新负载时的时间|在 cpu_load 得到更新时此成员记录了当前工作时间(jiffies 值)。
unsigned long nohz_flags;
用于在动态时钟模式开启时,记录当前时钟模式|不同模式切换会影响系统进 入不同的系统状态,通过先前系统状态和当前环境,系统决定进入不同的系统环境。
unsigned long last_sched_tick;
配置 CONFIG_NO_HZ_FULL 时,即动态时钟启用时,记录上一次调度 tick|动态时钟 管理中,调度并不周期性运行,因此需要成员记录调度时间。
struct load_weight load;
struct load_weight {
unsigned long weight;
u32 inv_weight;
}
用于跟踪就绪队列的负载,它是由 Local CPU 上所有可运行的调度实体共同作用的结果。成员 weight 用于记录具体权重,而 inv_weight 则用于优先级到权重的乘数因子. 一般来说,在每次 tick 中断到来时,系统更新就绪队列负载
unsigned long nr_load_updates;
当 rq->cpu_load[]成员更新时,该成员记录更新的次数。在函数 cpu_load_update ()中 更新此值,此函数由 cpu_load_update_periodic()调用,或者在配置 CONFIG_NO_HZ_COMMON 下由函数 cpu_load_update_nohz ()调用
u64 nr_switches;
用于跟踪就绪队列中进程上下文切换的次数。
想了解某个具体进程的切换次数和调度信息则可以通过/proc/PID/schedstat 和/proc/PID/sched 两个文件了解详细信息|
struct cfs_rq cfs;
struct rt_rq rt;
struct dl_rq dl;
分别对应:
CFS调度算法就绪队列
RT调度算法就绪队列
DL调度算法就绪队列
struct list_head leaf_cfs_rq_list;
如果需要 CFS 支持组调度管理,那就 得把所有 CFS 加入到一个链表当中,leaf_cfs_rq_list 成员就是负责把本 CPU 下的就绪队列 中各个 CFS 子队列关联起来。并且在 cfs_rq 里面有成员 on_list,其表示当前的 CFS 队列 leaf_cfs_rq_list 成员是否加入到就绪队列 rq 的 leaf_cfs_rq_list 管理的链表当中。
unsigned long nr_uninterruptible;
用于跟踪当前系统中队列里面不可信号中断的调度实体个数
struct task_struct *curr, *idle, *stop
分别用于描述当前正在运行的任务,空闲任务,刚刚停止运行的任务;
unsigned long next_balance
用于指示就绪队列进行下一次负载平衡的时间,该成员在初始化时被设置为当前系 统 jiffies。
struct mm_struct *prev_mm
就绪队列中用于保存先前执行任务使用的内存描述符 mm_struct 结构。此成员在发生调度时由内核函数 context_switch()来更新。
u64 clock;
描述的是队列的时间,一般在每个 tick 中 断到来时更新,其由上面提到的函数 update_rq_clock()对此成员更新。
u64 clock_task
clock_task 记录的是 队列中任务执行的时间,其由函数 update_rq_clock_task()负责更新。
atomic_t nr_iowait
描述当前就绪队列里面正在进行 IO 等待的进程个数,此成员一般和进程描述符 task_struct 中的 in_iowait 成员配合使用。
可以通过 proc/stat 下的 procs_blocked 值了解 nr_iowait 的大小
以上是rq基本成员,下面是SMP相关的成员:
struct llist_head wake_list;
描述了当前队列中需要唤醒的任务链表.
这个成员在 SMP 下非常重要,其是负载均衡时任务迁移中间状态链表.
对于那些需要迁移的任务来说,其会由本 CPU 下的就绪队列脱离出去,暂时的存 放到目标 CPU 下的就绪队列的 wake_list 链表当中。
struct root_domain *rd;
指示了当前 rq 运行队列可以工作的 CPU,即描述了可以为此就绪队列服务的 CPU。
struct sched_domain *sd
用于描述 rq 就绪队列所处的调度域。
unsigned long cpu_capacity
unsigned long cpu_capacity_orig
两个成员都是用于 rq 就绪队列的 CPU 能力描述。在系统初始化时,两者都被设置为 SCHED_CAPACITY_SCALE 值,当前版本中值大小为 1024。cpu_capacity 是运行过程中使用 的能力,而 cpu_capacity_orig 则是保存最原始的能力。可以使用函数 capacity_of()或者 capacity_orig_of()获取 rq 就绪队列的 CPU 能力值。用户可以调用函数 update_cpu_capacity()更新此能力。
struct callback_head *balance_callback;
在调度(_schedule())函数末尾处的回调链表,每个回调函数只会执行一次。
目前仅在RT/DL调度中使用。
int active_balance
用于指示是否需要开启负载均衡操作,在函数 load_balance()对其设置。在负载均衡操作中,如果此值被设置,则内核把任务迁移到空闲 CPU 上运行。
int push_cpu
用于指示可 push 任务所在的 CPU。在负载均衡操作中,对可 push 的 CPU 选取操 作是一个非常繁琐的计算过程,一般会选择空闲的 CPU 处理迁移的任务。
int cpu
多核环境中用于指示当前 rq 就绪队列所在的 CPU。
int online
rq 就绪队列是否处于工作状态,函数 set_rq_online()对其设置为 1,即表示为工作状态, 而函数 set_rq_offline()对其设置为 0,表示处于非工作状态。
struct list_head cfs_tasks
用于从就绪队列 rq 角度跟踪 CFS 就绪队列中的调度实体
u64 idle_stamp
用于记录负载平衡耗费的时间,这个时间是可以记录到空闲时间中去的。
u64 avg_idle
用于记录 CPU 空闲时间,此成员会与上面的 idle_stamp 配合使用。
u64 max_idle_balance_cost
此值与上面 avg_idle 作用类似,其用于跟踪 CPU 空闲的最大时间。