1、普通优先级:nice值 范围-20~+19,默认为0;越大意味着更低的优先级,linux下 nice代表时间片的比例。
2、实时优先级:范围0~99 任何实时优先级大于普通优先级。
#define MAX_USER_RT_PRIO 100//最大实时优先级
#define MAX_RT_PRIO MAX_USER_RT_PRIO
#define MAX_PRIO (MAX_RT_PRIO + NICE_WIDTH)
#define DEFAULT_PRIO (MAX_RT_PRIO + NICE_WIDTH / 2)
3、优先级的计算
动态优先级(task_struct->prio)
普通优先级 (task_struct->normal_prio)
静态优先级 (task_struct->static_prio)
static_prio是计算的起点,计算其他优先级则调用程序
set_user_nice->effective_prio
p->prio = effective_prio(p)
//实现
static int effective_prio(struct task_struct *p)
{
p->normal_prio = normal_prio(p);
/*如果实时优先级或者提高到实时优先级,则保持优先级不变,否则返回普通优先级*/
if (!rt_prio(p->prio))
return p->normal_prio;
return p->prio;
}
优先级总结如下
4、计算符合权重
根据nice值,计算得到的权重
const int sched_prio_to_weight[40] = {
/* -20 */ 88761, 71755, 56483, 46273, 36291,
/* -15 */ 29154, 23254, 18705, 14949, 11916,
/* -10 */ 9548, 7620, 6100, 4904, 3906,
/* -5 */ 3121, 2501, 1991, 1586, 1277,
/* 0 */ 1024, 820, 655, 526, 423,
/* 5 */ 335, 272, 215, 172, 137,
/* 10 */ 110, 87, 70, 56, 45,
/* 15 */ 36, 29, 23, 18, 15,
};
举例:
set_user_nice->set_load_weight
static void set_load_weight(struct task_struct *p, bool update_load)
{
int prio = p->static_prio - MAX_RT_PRIO;
struct load_weight *load = &p->se.load;
/*更新权重*/
if (update_load && p->sched_class == &fair_sched_class) {
reweight_task(p, prio);
} else {
//设置权重
load->weight = scale_load(sched_prio_to_weight[prio]);
load->inv_weight = sched_prio_to_wmult[prio];//2^32的值
p->se.runnable_weight = load->weight;
}
}
1、完全公平调度CFS:
SCHED_NORMAL :用于普通进程,CFS
SCHED_BATCH :相当于SCHED_NORMAL分化版本,采用分时策略,根据动态优先级,分配CPU运行需要资源
SCHED_IDLE:优先级最低,在系统空闲时间才执行
2、实时调度 :
SCHED_RR:轮流调度算法
SCHED_FIFO:先进先出,相同优先级任务先到先服务,高优先级任务可以抢占低优先级任务。
1)内核中的调度类
extern const struct sched_class stop_sched_class;
extern const struct sched_class dl_sched_class;
extern const struct sched_class rt_sched_class;//实时调度
extern const struct sched_class fair_sched_class;//完全公平调度
extern const struct sched_class idle_sched_class;
详细数据结构如下
struct task_struct {
int prio;//动态优先级
//静态优先级,进程启动时分配的优先级,,可以用Nice和sched_setscheduler系统调用修改,否则一直保持恒定
int static_prio;
int normal_prio;//动态优先级
//实时优先级 0-99 值越大优先级越高
unsigned int rt_priority;
//调度类
const struct sched_class *sched_class;
//调度实体
struct sched_entity se;
//实时调度实体
struct sched_rt_entity rt;
//idle 调度实体
struct sched_dl_entity dl;
unsigned int policy;//调度策略 SCHED_NORMAL SCHED_BATCH SCHED_IDLE SCHED_RR SCHED_FIFO
int nr_cpus_allowed;//限制进程可以再哪个处理器上运行
struct sched_info sched_info;//调度信息
...
}
调度类提供了通用调度器和各个调度方法之间的关联
struct sched_class {
const struct sched_class *next;//链表指针,当系统中有多个调度类,安装优先级排除排成一个链表
//进程加入到执行队列中,即将调度实体放入红黑树中
void (*enqueue_task) (struct rq *rq, struct task_struct *p, int flags);
void (*dequeue_task) (struct rq *rq, struct task_struct *p, int flags);
//放弃CPU执行权,实际上该函数执行先出对后入队,它直接将调度实体放在红黑树的最右端
void (*yield_task) (struct rq *rq);
bool (*yield_to_task)(struct rq *rq, struct task_struct *p, bool preempt);
//用于检查当前进程是否可被新进程抢占
void (*check_preempt_curr)(struct rq *rq, struct task_struct *p, int flags);
struct task_struct *(*pick_next_task)(struct rq *rq);
//选择下一个要运行的进程
void (*put_prev_task)(struct rq *rq, struct task_struct *p);
//将进程回放到运行队列中
void (*set_next_task)(struct rq *rq, struct task_struct *p, bool first);
#ifdef CONFIG_SMP
int (*balance)(struct rq *rq, struct task_struct *prev, struct rq_flags *rf);
//为进程选择一个合适的CPU
int (*select_task_rq)(struct task_struct *p, int task_cpu, int sd_flag, int flags);
//迁移到另外一个CPU
void (*migrate_task_rq)(struct task_struct *p, int new_cpu);
//用于唤醒进程
void (*task_woken)(struct rq *this_rq, struct task_struct *task);
//修改进程在CPU的亲和力
void (*set_cpus_allowed)(struct task_struct *p,
const struct cpumask *newmask);
//启动运行队列
void (*rq_online)(struct rq *rq);
//禁止运行队列
void (*rq_offline)(struct rq *rq);
#endif
//在每次激活周期调度时,由周期性调度器调用
void (*task_tick)(struct rq *rq, struct task_struct *p, int queued);
//进程创建时调用
void (*task_fork)(struct task_struct *p);
//进程退出是调用
void (*task_dead)(struct task_struct *p);
//进程切换
void (*switched_from)(struct rq *this_rq, struct task_struct *task);
void (*switched_to) (struct rq *this_rq, struct task_struct *task);
void (*prio_changed) (struct rq *this_rq, struct task_struct *task,
void (*update_curr)(struct rq *rq);
};
管理活动进程的主要数据结构rq 称为就绪队列。各个CPU都有自身的就绪队列,各个活动进程只出现在一个就绪队列中。
struct rq {
raw_spinlock_t lock;
unsigned int nr_running;//队列上可以运行的进程数目
unsigned long nr_load_updates;
u64 nr_switches;
struct cfs_rq cfs;//cfs调度
struct rt_rq rt;//实时调度
struct dl_rq dl;//空闲调度
unsigned long nr_uninterruptible;
struct task_struct __rcu *curr;
struct task_struct *idle;
struct task_struct *stop;
unsigned long next_balance;
struct mm_struct *prev_mm;
unsigned int clock_update_flags;
u64 clock;//用于实现就绪队列自身的时钟,每次调用周期性调度器时,都会更新clock值
u64 clock_task ____cacheline_aligned;
u64 clock_pelt;
unsigned long lost_idle_time;
atomic_t nr_iowait;
}
//task_struct中内嵌sched_entity 所以进程是可调度实体
struct sched_entity {
struct load_weight load;//制定了权重,决定了各个实体占队列总负荷的比例
unsigned long runnable_weight;
struct rb_node run_node;//红黑树节点,使得实体可以在红黑树上排序
struct list_head group_node;
unsigned int on_rq;//该实体是否 在就绪队列上接收调度
u64 exec_start;//更新到当前时间
u64 sum_exec_runtime;//差值
u64 vruntime;//虚拟时钟上流逝的时间
u64 prev_sum_exec_runtime;//进程被撤销CPU时,将sum_exec_runtime保存到prev_sum_exec_runtime,而sum_exec_runtime持续增长
...
}