当前位置: 首页 > article >正文

Linux内核源码分析-进程调度(二)-常用数据结构

cfs调度器常用数据结构

  • struct task_struct
  • struct sched_entity
  • struct rq
  • struct cfs_rq

struct task_struct

Linux通过struct task_struct结构体描述每一个进程。task_struct包含很多进程相关的信息(例如,优先级、进程状态以及调度实体等)。简略版的task_struct如下:

// 进程描述符
struct task_struct {
	/* -1 unrunnable, 0 runnable, >0 stopped: */
	volatile long			state;  // 进程状态标志
#endif
	/*
	 * /* task_struct::on_rq states: */
     * #define TASK_ON_RQ_QUEUED	1  // 表示进程正在就绪队列中运行。
     * #define TASK_ON_RQ_MIGRATING	2  // 表示处于迁移过程中的进程,可能不在就绪队列中。
	 */
	int				on_rq;  // 调度实体是否在就绪队列中,0代表不存在,1代表存在。

    // 进程调度策略和优先级
    /*
	  限期进程的优先级比实时进程要高,实时进程的优先级比普通进程要高.
	        限期进程的优先级是-1;
			实时进程的优先级0-99, 优先级数值越大,表示优先级越高;
			普通进程的优先级为: 100-139, 优先级数值越小,表示优先级越高, 可通过修改nice值改变普通进程的优先级,  优先级等于120加上nice值.                                                                                                                                                                                                                                          ,想
	*/
    /*
	  static_prio:普通进程的静态优先级,值越小优先级越高。静态优先级是进程启动时分配的优先级。
	               可以用nice()或者sched_setscheduler()系统调用更改,否则在运行期间一直保持不变。
	  rt_priority:实时进程的优先级,值介于[0,99]之间。注意:rt_priority是值越大优先级越高
	  normal_prio:基于前两个参数ststic_prio和rt_priority计算出来的。
	               可以这样理解:static_prio和rt_priority分别代表普通进程和实时进程的“静态(原生)”的优先级,代表进程的固有属性。
				   由于普通进程是值越小优先级越高,而实时进程是值越大优先级越高,需要统一成值越小优先级越高,因此普通进程的normal_prio等于static_prio,而实时进程的normal_prio等于99-rt_priority。
	  prio:进程的有效优先级,也称动态优先级。
	        顾名思义,在系统中需要判断进程优先级时用的便是该参数,调度考虑的优先级也是它。对于实时进程来说,有效优先级prio通常就等于normal_prio。
			进程可以临时提高优先级,通过改变prio的值实现,有效优先级的提升不影响进程的静态优先级。子进程的有效优先级继承自父进程的静态优先级,而不是父进程的有效优先级(父进程的优先级临时提高了,该特性不会遗传给子进程)。
	*/

	int				prio;   // 调度优先级(数值越小, 优先级越高)
	int				static_prio;   // 静态优先级
	int				normal_prio;  // 正常优先级(数值越小, 优先级越高)
	unsigned int			rt_priority;  // 实时优先级,用来记录实时进程的用户空间的静态优先级。

	const struct sched_class	*sched_class;  // 表示该进程所属的调度器类
	struct sched_entity		se;  // 普通进程调度类实体
	struct sched_rt_entity		rt;  // 实时进程调度类实体
#ifdef CONFIG_CGROUP_SCHED
	struct task_group		*sched_task_group;  // 组调度
#endif
	struct sched_dl_entity		dl;  // deadline调度类实体
}

struct sched_entity

但是,每一个调度类并不是直接管理task_struct,管理的是调度实体,从而引入调度实体的概念。CFS调度器使用sched_entity达到PELT(per entity load tracking),即se级别的负载跟踪。调度实体记录权重信息、运行时间信息、负载信息等。

// 被调度实体(包括两种:①与进程一一对应的调度实体,即一个进程一个调度实体;②组调度实体,一个组对应一个调度实体,一个组里可能包含多个进程(即当前group se包含多个task se))。
struct sched_entity {
	/* For load-balancing: */
	struct load_weight		load;  // 权重信息,用于计算虚拟时间。

	/*
	 * runnable_weight指可运行权重,该概念主要针对group se提出。
	 * 针对task se来说,runnable_weight的值就是和进程权重weight相等。
	 * 针对group se,runnable_weight总是小于等于weight (其实这话说的像废话,因为一个group se中还有可能存在running状态的task se)。
	 */
	unsigned long			runnable_weight;  // 在所有可运行进程中所占的权重。
	struct rb_node			run_node;  // 调度实体作为一个节点插入到CFS的红黑树中。CFS调度器的每个就绪队列维护了一颗红黑树,上面挂满了就绪等待执行的task,run_node就是挂载点。
	struct list_head		group_node;  // 当前调度实体属于哪一个调度组,是rq中cfs_tasks链表的挂载点。
	unsigned int			on_rq;  // 是否在就绪队列上。进程加入到就绪队列,该位置被置为1;退出就绪队列,被置为0。

	u64				exec_start;  // 上次启动时间
	u64				sum_exec_runtime;  // 当前进程总的CPU消耗时间,这个是真实的CPU消耗时间。
	u64				vruntime;  // 虚拟运行时间
	u64				prev_sum_exec_runtime;  // 进程撤销时会将sum_exec_runtime保存到prev_sum_exec_runtime中。

	u64				nr_migrations;

	struct sched_statistics		statistics;

#ifdef CONFIG_FAIR_GROUP_SCHED  // 组调度启用宏
	int				depth;  // se的嵌套深度,比如cpu rq的顶层调度实体为task se,则depth等于0;若为greou se中的task se,则此时的task se中的depth为1。
	struct sched_entity		*parent;  // 当前调度实体是task se时,parent指向包含此task se的group se。
	/* rq on which this entity is (to be) queued: */
	struct cfs_rq			*cfs_rq;  // 所属的就绪队,即指向group se的就绪队列cfs_rq。
	/* rq "owned" by this entity/group: */
	struct cfs_rq			*my_q;  // group se的就绪队列。
#endif

#ifdef CONFIG_SMP
	/*
	 * Per entity load average tracking.
	 *
	 * Put into separate cache line so it does not
	 * collide with read-mostly values above.
	 */
	struct sched_avg		avg;  // 负载信息
#endif
};

struct rq

// 每个CPU都有一个全局的运行队列,它是per-cpu类型,即每个cpu上都会有一个struct rq结构体。rq中包含cfs就绪队列、rt就绪队列、dl就绪队列。

struct rq { 
	unsigned int		nr_running;  // rq中可运行的进程数量
	struct cfs_rq		cfs;  // cfs就绪队列
	struct rt_rq		rt;  // rt就绪队列
	struct dl_rq		dl;  // dl就绪队列
};

struct cfs_rq

CFS调度器的就绪队列,简称cfs就绪队列,管理就绪态的struct sched_entity调度实体,后续可以通过pick_next_task接口从cfs就绪队列中选择最适合运行的调度实体(虚拟时间最小的调度实体)。
cfs就绪队列包含队列权重、负载、运行统计等信息。

/* CFS-related fields in a runqueue */
// 
struct cfs_rq {
	struct load_weight	load;  // 就绪队列权重,即就绪队列管理的所有调度实体权重之和。
	unsigned long		runnable_weight;  // 针对group se提出,是实体权重的一部分,表示rq中可运行实体的权重总和。对于task se来说,runnable_weight就是se的weight,二者的值完全一样。
	unsigned int		nr_running;  // 就绪队列上调度实体的个数(包括task se和group se,比如task se个数为9,group se个数为1,则nr_running为10)。

	// 就绪队列上真实的调度实体的个数(比如当前调度队列上task se个数为9,group se个数为1(group se中包含9个task se,则),则h_nr_running为18)
	unsigned int		h_nr_running;  /* SCHED_{NORMAL,BATCH,IDLE} */  
	unsigned int		idle_h_nr_running; /* SCHED_IDLE */

	u64			exec_clock;

	/*
	 * cfs_rq(csf run queue)中的每一个进程都有一个虚拟时钟,vruntime。
	 * 如果一个进程得以执行,随着时间的增加(一个一个tick的到来),其vruntime将不断增大。
	 * 没有得到执行的进程vruntime不变。调度器总是选择vruntime最小的那个进程来运行(即红黑树最左边的进程)。这就是所谓的“完全公平”。
	 * 为了区别不同优先级的进程,优先级高的进程vruntime增长得慢,以至于它们可以得到更多的运行时间。
	*/
	u64			min_vruntime;  // 跟踪就绪队列上所有调度实体的最小虚拟运行时间。
#ifndef CONFIG_64BIT
	u64			min_vruntime_copy;
#endif

	struct rb_root_cached	tasks_timeline;  // 按照虚拟时间大小排序的红黑树(虚拟运行时间最小的在红黑树的最左边)。

	/*
	 * 'curr' points to currently running entity on this cfs_rq.
	 * It is set to NULL otherwise (i.e when none are currently running).
	 */
	//  sched_entity:可被内核调度的实体
	struct sched_entity	*curr;  // 就绪队列的当前执行进程的可调度实体
	struct sched_entity	*next;
	struct sched_entity	*last;
	struct sched_entity	*skip;

#ifdef	CONFIG_SCHED_DEBUG
	unsigned int		nr_spread_over;
#endif

#ifdef CONFIG_SMP
	/*
	 * CFS load tracking
	 */
	struct sched_avg	avg;  // 负载信息
#ifndef CONFIG_64BIT
	u64			load_last_update_time_copy;
#endif


    /*
	 * 当一个任务退出或者唤醒后迁移到到其他cpu上的时候,那么原本所在CPU的cfs rq上需要移除该任务带来的负载。
	 * 由于持rq锁问题,所以先把移除的负载记录在这个removed成员中,适当的时机再更新之。删除记录函数 remove_entity_load_avg(),实际删除函数 update_cfs_rq_load_avg()。
	 */
	struct {
		raw_spinlock_t	lock ____cacheline_aligned;
		int		nr;
		unsigned long	load_avg;
		unsigned long	util_avg;
		unsigned long	runnable_sum;
	} removed;

#ifdef CONFIG_FAIR_GROUP_SCHED

    /*
	 * 指该group cfs_rq已经向 task group 的load_avg同步的负载值。
	 * 因为tg是一个全局共享变量,多个cpu会同时访问,为了避免严重的资源竞争,group cfs_rq的负载贡献值(运行就更新)不会立即就更新到tg->load_avg(task group的负载总和)。
	 * 而是等到差值大到一定程度,才会再次同步到tg->load_avg;
	 */
	unsigned long		tg_load_avg_contrib;
	long			propagate;
	long			prop_runnable_sum;

	/*
	 *   h_load = weight * f(tg)
	 *
	 * Where f(tg) is the recursive weight fraction assigned to
	 * this group.
	 */
	unsigned long		h_load;
	u64			last_h_load_update;
	struct sched_entity	*h_load_next;
#endif /* CONFIG_FAIR_GROUP_SCHED */
#endif /* CONFIG_SMP */

#ifdef CONFIG_FAIR_GROUP_SCHED
	struct rq		*rq;	/* CPU runqueue to which this cfs_rq is attached ,此cfs_rq附加到的CPU运行队列,即cfs队列所在的全局就绪队列*/

	/*
	 * leaf cfs_rqs are those that hold tasks (lowest schedulable entity in
	 * a hierarchy). Non-leaf lrqs hold other higher schedulable entities
	 * (like users, containers etc.)
	 *
	 * leaf_cfs_rq_list ties together list of leaf cfs_rq's in a CPU.
	 * This list is used during load balance.
	 */
	int			on_list;
	struct list_head	leaf_cfs_rq_list;

	// tg指向就绪队列包含的调度实体属于的task group。
	struct task_group	*tg;	/* group that "owns" this runqueue */  

#ifdef CONFIG_CFS_BANDWIDTH
	int			runtime_enabled;
	s64			runtime_remaining;

	u64			throttled_clock;
	u64			throttled_clock_task;
	u64			throttled_clock_task_time;
	int			throttled;
	int			throttle_count;
	struct list_head	throttled_list;
#endif /* CONFIG_CFS_BANDWIDTH */
#endif /* CONFIG_FAIR_GROUP_SCHED */
};

http://www.kler.cn/a/14303.html

相关文章:

  • P1042 [NOIP2003 普及组] 乒乓球
  • Go | 一分钟掌握Go | 2 - 集成开发工具
  • 科学防雷接地和雷电防护方案
  • 本地运行 minigpt-4
  • TCP FACK 与 RACK
  • 改进YOLOv8 | 即插即用篇 | YOLOv8 引入 RepVGG 重参数化模块 |《RepVGG:让VGG风格的卷积神经网络再次伟大》
  • Ansys Zemax | 设计抬头显示器时要使用哪些工具 – 第一部分
  • Adobe国际认证证书,深化设计师个人优势!
  • shell脚本备份postgres SQL数据库(同台服务器备份脚本)
  • 前端异步编程全套:xmlhttprequest > ajax > promise > async/await
  • 打造高效团队利器,掌握必备技能,揭秘完美工具箱——DevOps!
  • 运用SQL查询mysql,sersql,oracle所有数据库名和表名
  • 今天主要谈谈关于申请美国专利的一些问题
  • 数据库基础篇 《17.触发器》
  • Hadoop2.x集群搭建(centos7、VMware、finalshell)
  • 丁鹿学堂:2023最新vue3学习指南(vue3最简单demo)
  • 【致敬未来的攻城狮计划】— 连续打卡第十天:FSP固件库开发及FSP配置详解。
  • 20230425----重返学习-vue项目-vue自定义指令-vue-cli的配置
  • CentOS 7中安装配置Nginx的教程指南
  • linux waitpid函数详解