task_struct
struct task_struct {
int prio, static_prio, normal_prio;
unsigned int rt_priority;
struct list_head run_list;
const struct sched_class *sched_class;
struct sched_entity se;
unsigned int policy;
cpumask_t cpus_allowed;
unsigned int time_slice;
}
- 首先是进程的优先级别, 分为两种一种是静态的的优先级, 另外一种是动态的优先级
rt_priority
表示实时进程的优先级sched_class
表示该进程所属的调度器类- 调度器不限于调度进程,还可以处理更大的实体。这可以用于实现组调度:可用的CPU时间 可以首先在一般的进程组(例如,所有进程可以按所有者分组)之间分配,接下来分配的时 间在组内再次分配。
policy
保存了对该进程应用的调度策略。cpus_allowed
是一个位域,在多处理器系统上使用,用来限制进程可以在哪些CPU上运行run_list
和time_slice
是循环实时调度器所需要的,但不用于完全公平调度器。run_list 是一个表头,用于维护包含各进程的一个运行表,而time_slice则指定进程可使用CPU的剩余时间段
sched_class
调度器类提供了多个通用调度器和各个调度方法之间的关联
struct sched_class {
const struct sched_class *next;
void (*enqueue_task) (struct rq *rq, struct task_struct *p, int wakeup);
void (*dequeue_task) (struct rq *rq, struct task_struct *p, int sleep);
void (*yield_task) (struct rq *rq);
void (*check_preempt_curr) (struct rq *rq, struct task_struct *p);
struct task_struct * (*pick_next_task) (struct rq *rq);
void (*put_prev_task) (struct rq *rq, struct task_struct *p);
void (*set_curr_task) (struct rq *rq);
void (*task_tick) (struct rq *rq, struct task_struct *p);
void (*task_new) (struct rq *rq, struct task_struct *p);
}
- enqueue_task向就绪队列添加一个新进程, 在进程从睡眠态变成可以运行的状态的时候,
- dequeue_task将一个进程从系统中去除, 进程从可运行状态切换到不可运行状态
- yield_task 当操作系统放弃控制权力的时候, 退出
- check_preempt_curr这个函数表示使用一个新唤醒的进程来抢占当前的进程
- pick_next_task用来选择下一个将要运行的进程
- 当进程的调度策略发生变化的时候, 需要调用set_cur_task
- task_task在每次激活周期性调度器的时候, 由周期性调度器调用
- new_task用于建立fork系统调用和调度器之间的关联, 每次新的进程创建的时候, 使用new_task通知调度器
rq
核心调度器用于管理活动进程的主要进程的数据结构,各个CPU都有自己的调度器, 活动进程只能存在于一个就绪队列中
struct rq {
unsigned long nr_running;
#define CPU_LOAD_IDX_MAX 5
unsigned long cpu_load[CPU_LOAD_IDX_MAX];
struct cfs_rq cfs;
struct rt_rq rt;
struct task_struct* curr, *idle;
};
- nr_runing 指定队列上可以运行的进程数量, 不考虑优先级别或者调度类
- load 提供了就绪队列当前的负荷的度量
- cpu_load用于跟踪此前的负荷状态
- cfs和rt是嵌入时的子就绪队列, 分别用于完全公平调度器和实时调度器
- curr指向当前运行的进程的task_struct实例
- idle指向idle进程的task_struct,
- clock和prev_raw_clock用于实现就绪队列自身的时钟, 每次调用周期性调度器的时候,都会更新对应的值,
系统中所有的就绪队列都位于一个runqueues数组中, 这个数组的每个元素分别对应于系统中的一个CPU
static DEFINE_PER_CPU_SHARED_ALIGNED(struct rq, runqueues);
sched_entity
struct sched_entity {
struct load_weight load; /* 用于负载均衡 */
struct rb_node run_node;
unsigned int on_rq;
u64 exec_start;
u64 sum_exec_runtime;
u64 vruntime;
u64 prev_sum_exec_runtime;
}
- load指定了权重,决定了各个实体占队列总负荷的比例。计算负荷权重是调度器的一项重任, 因为CFS所需的虚拟时钟的速度最终依赖于负荷
- runnode是标准的树节点,让实体可以在红黑树上进行排序
- on_req表示实体当前是否在就绪队列上接受调度
- sum_exec_runtime 在进程运行的, 我们需要记录消耗CPU的时间, 用于完全公平调度器
- exec_start是当前的时间
- 在进程执行期间虚拟时钟上面流失的时间数量由vruntime进行统计
- 在进程被撤销CPU时, 其当前sum_execve_runtime值保存到prev_exec_runtime,之后进程抢占时候又需要这个参数。
处理优先级别
- 在用户空间, 我们可以使用nice这个系统调用来设置用户进程的优先级别,nice值在-20和-19之间, 它的值越低, 表明优先级别越高。
内核使用一些简单些的数值范围比如从0到139用来表示内部优先级别。**其中1-99是用于实时进程的调度, nice值的映射是[100, 139]。
在sched.h这个文件中有一些比较常用的宏用于不同形式之间的转换.
#define MAX_USER_RT_PRIO 100
#define MAX_RT_PRIO MAX_USER_RT_PRIO
#define MAX_PRIO (MAX_RT_PRIO + 40)