Linux内核源码分析 (4)实时调度类及SMP

Linux内核源码分析 (4)实时调度类及SMP

一、实时调度类分析

  • 实时进程与普通进程有一个根本的不同之处:如果系统中有一个实时进程且可运行,那么调度器总是会选中它运行,除非有另一个优先级更高的实时进程。
  • 现有的两种实时类,不同之处如下所示。
    • 循环进程SCHED_RR)有时间片,其值在进程运行时会减少,就像是普通进程。在所有的时间段都到期后,则该值重置为初始值,而进程则置于队列的末尾。这确保了在有几个优先级相同的SCHED_RR进程的情况下,它们总是依次执行。
    • 先进先出进程SCHED_FIFO)没有时间片,在被调度器选择执行后,可以运行任意长时间。

调度器数据结构

  • 调度器使用一系列数据结构,来排序和管理系统中的进程。调度器的工作方式与这些结构的设计密切相关。几个组件在许多方面彼此交互。下面概述了这些组件的关联。下文中将这两个组件称为通用调度器generic scheduler)或核心调度器core scheduler)。
    • 主调度器:通过调用schedule()函数来完成进程的选择和切换。
    • 周期性调度器:根据固定频率自动调用scheduler_tick()函数,不时检测是否有必要进行进程切换。
    • 上下文切换:主要做两个事情(切换地址空间、切换寄存器和栈空间)
  • 调度器类用于判断接下来运行哪个进程。内核支持不同的调度策略(完全公平调度、实时调度、在无事可做时调度空闲进程),调度类使得能够以模块化方法实现这些策略,即一个类的代码不需要与其他类的代码交互。在调度器被调用时,它会查询调度器类,得知接下来运行哪个进程。
  • 在选中将要运行的进程之后,必须执行底层任务切换。这需要与CPU的紧密交互。
  • 每个进程都刚好属于某一调度类,各个调度类负责管理所属的进程。通用调度器自身完全不涉及进程管理,其工作都委托给调度器类。

task_struct中与调度有关的的成员

include/linux/<sched.h>

struct task_struct { 
	... 
	/*prio和normal_prio表示动态优先级,static_prio表示进程的静态优先级*/
	int prio, static_prio, normal_prio; 

	/*表示实时进程的优先级,范围是[0,99]*/
	unsigned int rt_priority; 
	
	/*表示该进程所属的调度器类*/
	const struct sched_class *sched_class; 

	/*调度实体的实例。注意:调度器不限于调度进程,还可以处理更大的实体。这可以用于实现组调度:可用的CPU时间
	可以首先在一般的进程组(例如,所有进程可以按所有者分组)之间分配,接下来分配的时间在组内再次分配。*/
	struct sched_entity se; 

	/*policy保存了对该进程应用的调度策略,Linux支持5种可能的值:
		SCHED_NORMAL:	通过完全公平调度器来处理,用于普通进程
		SCHED_BATCH:	通过完全公平调度器来处理,用于非交互、CPU使用密集的批处理进程,不会干扰交互式进程
		SCHED_IDLE:	通过完全公平调度器来处理,其相对权重总是最小的。
		SCHED_RR:		通过实时调度器处理,用于实现软实时进程,实现了一种循环方法
		SCHED_FIFO:	通过实时调度器处理,用于实现软实时进程,实现了一种先进先出机制。
	*/
	unsigned int policy; 

	/*一个位域,在多处理器系统上使用,用来限制进程可以在哪些CPU上运行*/
	cpumask_t cpus_allowed; 

	/*是一个表头,用于维护包含各进程的一个运行表,该成员实时调度器需要,不用于CFS*/
	struct list_head run_list; 

	/*指定进程可使用CPU的剩余时间段,该成员实时调度器需要,不用于CFS*/
	unsigned int time_slice; 
	... 
}

就绪队列

  • 核心调度器用于管理活动进程的主要数据结构称之为就绪队列。各个CPU都有自身的就绪队列,各个活动进程只出现在一个就绪队列中。在多个CPU上同时运行一个进程是不可能的(但发源于同一进程的各线程可以在不同处理器上执行,因为进程管理对进程和线程不作重要的区分)。
  • 注意,进程并不是由就绪队列(rq)的成员直接管理的!这是各个调度器类(如stop_sched_classdl_sched_classrt_sched_classfair_sched_classidle_sched_class)的职责,因此在各个就绪队列中嵌入了特定于调度器类的子就绪队列(struct cfs_rq cfs;struct rt_rq rt;)。
  • 就绪队列主要使用下列数据结构实现
    kernel/sched.c
    struct rq { 
    	/*指定了队列上可运行进程的数目,不考虑其优先级或调度类*/
    	unsigned long nr_running; 
    ... 
    	/*提供了就绪队列当前负荷的度量,队列的负荷本质上与队列上当前活动进程的数目成正比。*/
    	struct load_weight load; 
    	
    	#define CPU_LOAD_IDX_MAX 5 
    	/*用于跟踪此前的负荷状态*/
    	unsigned long cpu_load[CPU_LOAD_IDX_MAX]; 
    	
    	/*cfs和rt是嵌入的子就绪队列,分别用于完全公平调度器和实时调度器。*/
    	struct cfs_rq cfs; 
    	struct rt_rq rt; 
    
    	/*curr指向当前运行的进程的task_struct实例。idle指向idle进程的task_struct实例,
    	该进程亦称为idle线程,在无其他可运行进程时执行*/
    	struct task_struct *curr, *idle; 
    
    	/*用于实现就绪队列自身的时钟。每次调用周期性调度器时,都会更新clock的值。*/
    	u64 clock; 
    ... 
    };
    
  • 系统的所有就绪队列都在runqueues数组中,该数组的每个元素分别对应于系统中的一个CPU。在单处理器系统中,由于只需要一个就绪队列,数组只有一个元素。
    kernel/sched.c
    static DEFINE_PER_CPU_SHARED_ALIGNED(struct rq, runqueues);
    

调度实体

  • 由于调度器可以操作比进程更一般的实体(进程嵌入了sched_entity实例,所以进程是可调度实体,还有更大的可调度实体比如组调度等 ),因此需要一个适当的数据结构来描述此类实体。其定义如下:
    include/linux/<sched.h>
    struct sched_entity { 
    	/*用于负载均衡,决定了各个实体占队列总负荷的比例*/
    	struct load_weight load; 
    
    	/*run_node是标准的树结点,使得实体可以在红黑树上排序*/
    	struct rb_node run_node; 
    
    	/*表示该实体当前是否在就绪队列上接受调度*/
    	unsigned int on_rq; 
    	
    	/*记录消耗的CPU时间,以用于完全公平调度器。跟踪运行时间是由update_curr不断累积完成的,调度器中许多地方都会调用该函数,例如,新进程加入就绪队列时,或者周期性调度器中。每次调用时,会计算当前时间和exec_start之间的差值,exec_start则更新到当前时间。差值则被加到sum_exec_runtime。*/
    	u64 sum_exec_runtime; 
    	u64 exec_start; 
    
    	/*在进程执行期间虚拟时钟上流逝的时间数量*/
    	u64 vruntime; 
    
    	/*在进程被撤销CPU时,其当前sum_exec_runtime值保存到prev_exec_runtime。此后,在进程抢占时又需要该数据。
    		注意:此过程并不会更新sum_exec_runtime。*/
    	u64 prev_sum_exec_runtime; 
    ... 
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Elec Liu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值