调度策略通常在进程 响应速度最大系统利用率寻找平衡。

进程调度框图如下
在这里插入图片描述

一、进程优先级

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,
};

举例:

  • A B两个进程,nice都为0 则每个进程的份额1024/(1024+1024)=50%,则即每个进程得到cpu的时间各50%.
  • A ,B两个进程,A的nice=0,B的nice=1;
    则A 1024/(1024+820)=0.55%,B 820/(1024+820)=0.45%,这样产生10%的差额。

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;

2)进程调度相关结构体之间的关系
在这里插入图片描述

详细数据结构如下

三、task_struct 与调度相关的成员

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持续增长
...
}
GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:23 天前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐