目录
二、完全公平调度器(Completely Fair Scheduler, CFS)
一、Linux内核中的调度器
1、调度器
1.1、概念
在Linux内核中,调度器(Scheduler)是操作系统内核的关键组件之一,负责管理和协调CPU资源在多个进程间高效、公正地分配。调度器的主要目标是确保CPU时间片能在系统中的各个进程间合理分配,从而最大化系统资源利用率,同时也要考虑进程的响应时间和整体系统性能。
1.2、结构框图
Linux内核中用来安排调度进程 (一段程序的执行过程) 执行的模块称为调度器(Scheduler),它可以切换进程状态 (Process status) 。比如: 执行、可中断睡眠、不可中断睡眠、退出、暂停等。
调度器是 CPU 中央处理器的管理员,主要负责完成做两件事情:一、选择某些就绪进 程来执行。二、是打断某些执行的进程让它们变为就绪状态。调度器分配CPU 时间的基本依据:进程的优先级。上下文 切换(context switch ):将进程在 CPU 中切换执行的过程,内核承担此任务,负责重建和存储被切换掉之前的CPU 状态。
1.3、Linux下的调度类介绍(sched_class)
①、sched_class数据结构介绍
调度类是内核调度框架中更为抽象的概念,它是一种组织和管理调度策略的结构化方式。在Linux内核中,每个调度类都对应一个具体的调度策略,或者一套相关的调度策略,并提供了一系列与调度相关的函数接口,如进程入队、出队、选择下一个运行进程等操作。
sched_class结构体是在代码中体现调度类,描述了调度器的基本操作接口,内核根据不同调度策略(如CFS、实时调度等)实现了对应的调度类,这些调度类则通过实现下面的接口来参与到内核的整体调度流程中。
数据结构定义在kernel/sched/sched.h
struct sched_class {
#ifdef CONFIG_UCLAMP_TASK
int uclamp_enabled;
#endif
/* 将进程加入到执行队列当中,即将调度实体(进程)存放到红黑树中,并对nr_running变量自动会加1*/
void (*enqueue_task) (struct rq *rq, struct task_struct *p, int flags);
/* 从执行队列当中删除进程,并对nr_running变量自动减1 */
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);
/* 用于检查当前进程是否可被新进程抢占 */
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 flags);
struct task_struct * (*pick_task)(struct rq *rq);
/* 迁移任务到另一个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,
u32 flags);
/* 启动运行队列 */
void (*rq_online)(struct rq *rq);
/* 禁止运行队列 */
void (*rq_offline)(struct rq *rq);
struct rq *(*find_lock_rq)(struct task_struct *p, struct rq *rq);
#endif
/* 调用自time_tick函数,它可能引起进程切换,将驱动运行时(running)抢占 */
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);
/*
* The switched_from() call is allowed to drop rq->lock, therefore we
* cannot assume the switched_from/switched_to pair is serialized by
* rq->lock. They are however serialized by p->pi_lock.
*/
/* 专门用于进程的切换*/
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,
int oldprio);
unsigned int (*get_rr_interval)(struct rq *rq,
struct task_struct *task);
void (*update_curr)(struct rq *rq);
#ifdef CONFIG_FAIR_GROUP_SCHED
/* */
void (*task_change_group)(struct task_struct *p);
#endif
};
比较重要的几个成员接口:
- enqueue_task: 向就绪队列添加一个进程,某个任务进入可运行状态时,该函数将会调用,它将调度实体放入到红黑树当中。
- dequeue_task: 将一个进程从就绪队列中进行删除,当某个任务退出可运行状态时调用该函数,将从红黑树中去掉对应调度实体。
- yield_task:在进程想要资源放弃对处理器的控制权时,可使用在sched_yiled系统调用,会调用内核API去处理操作。
- check_preempt_curr