进程调度之就绪队列

核心调度器管理活动进程的主要数据结构是就绪队列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 空闲的最大时间。

你可能感兴趣的:(进程调度之就绪队列)