内核源码阅读(八)进程调度器的实现
2017-08-20 12:26
393 查看
调度器的任务就是使程序之间共享CPU时间,创造并行执行的假象。其可分为两个方面:一是调度策略;二是上下文切换。
1.总览
一般原理:按所能分配的计算能力,向系统中每个进程提供最大的公正性。
调度器对于进程等待时间的记录如下图所示。所有的可运行程序都按时间在红黑树中排序。就绪队列装备了虚拟时钟,其精确速度依赖于当前等待调度器挑选的进程的数目,约为实时时钟速度的1/4。
2.数据结构
调度器子系统各组件概观如下图所示
激活调度方法:
1)直接由进程放弃CPU;
2)周期性机制,以固定频率运行,不时检测是否进行进程切换。
task_struct中与调度相关的成员:
调度器类
其结构如下:
就绪队列
核心调度器用于管理活动进程的主要数据结构称之为就绪队列。每个CPU都有自己的就绪队列,一个进程不可能出现在多个就绪队列,即不能多个CPU不能运行一个进程。
就绪队列的结构如下所示。
调度实体
调度器的操作实体结构如下:
1.总览
一般原理:按所能分配的计算能力,向系统中每个进程提供最大的公正性。
调度器对于进程等待时间的记录如下图所示。所有的可运行程序都按时间在红黑树中排序。就绪队列装备了虚拟时钟,其精确速度依赖于当前等待调度器挑选的进程的数目,约为实时时钟速度的1/4。
2.数据结构
调度器子系统各组件概观如下图所示
激活调度方法:
1)直接由进程放弃CPU;
2)周期性机制,以固定频率运行,不时检测是否进行进程切换。
task_struct中与调度相关的成员:
<sched.h> struct task_struct { ...... int prio, static_prio, normal_prio; unsigned int rt_priority; struct list_head run_list; const struct sched_class *sched_class; struct sched_entity se; unsigned int policy; cpumask_t cpus_allowed; unsigned int time_slice; ..... } prio、normal_prio:进程动态优先级 static_prio:进程静态优先级,在进程启动时就被分配,可通过nice和sched_setscheduler函数进行修改。 rt_priority:表示实时进程优先级。最高优先级99,最低优先级0。 sched_class:表示该进程所属的调度类。 policy:保存进程调度策略,Linux可取值为:SCHED_NORMAL用于普通进程,SCHED_BATCH用于非交互、CPU使用密集的批处理进程,SCHED_IDLE权重相对较小,SCHED_FIFO使用先进先出机制,SCHED_RR使用循环方法,两个都用于软实时进程。 cpus_allow:位域,用来限制执行进程的CPU。 run_list:用于维护包含各进程的一个运行表 time_slice::指定进程可使用CPU的剩余时间段。
调度器类
其结构如下:
1. struct sched_class { 2. const struct sched_class *next; 3. /* 向就绪队列添加新进程*/ 4. void (*enqueue_task) (struct rq *rq, struct task_struct *p, int flags); /* 从就绪队列中删除一个进程*/ 5. void (*dequeue_task) (struct rq *rq, struct task_struct *p, int flags); /* 进程自愿放弃对处理器的控制权时调用sched_yield->yield_task */ 6. void (*yield_task) (struct rq *rq); 7. /* 用一个新唤醒的进程类抢占当前进程 */ 8. void (*check_preempt_curr) (struct rq *rq, struct task_struct *p, int flags); 9. /* 选择下一个要运行的进程*/ 10. struct task_struct * (*pick_next_task) (struct rq *rq); 11. /* 用一个进程替代当前运行的进程 */ 12. void (*put_prev_task) (struct rq *rq, struct task_struct *p); 14.#ifdef CONFIG_SMP /* 选择就绪队列*/ 15. int (*select_task_rq)(struct rq *rq, struct task_struct *p, 16. int sd_flag, int flags); 17. 18. void (*pre_schedule) (struct rq *this_rq, struct task_struct *task); 19. void (*post_schedule) (struct rq *this_rq); 20. void (*task_waking) (struct rq *this_rq, struct task_struct *task); 21. void (*task_woken) (struct rq *this_rq, struct task_struct *task); 22. 23. void (*set_cpus_allowed)(struct task_struct *p, 24. const struct cpumask *newmask); 25. 26. void (*rq_online)(struct rq *rq); 27. void (*rq_offline)(struct rq *rq); 28.#endif 29. /* 设置当前执行的进程 */ 30. void (*set_curr_task) (struct rq *rq); 31. /* 在每次激活周期性调度器时,由周期性调度器调用 */ 32. void (*task_tick) (struct rq *rq, struct task_struct *p, int queued); 33. /* 创建一个新进程*/ 34. void (*task_fork) (struct task_struct *p); 35. 36. void (*switched_from) (struct rq *this_rq, struct task_struct *task, 37. int running); 38. void (*switched_to) (struct rq *this_rq, struct task_struct *task, 39. int running); 40. void (*prio_changed) (struct rq *this_rq, struct task_struct *task, 41. int oldprio, int running); 42. 43. unsigned int (*get_rr_interval) (struct rq *rq, 44. struct task_struct *task); 45. 46.#ifdef CONFIG_FAIR_GROUP_SCHED 47. void (*task_move_group) (struct task_struct *p, int on_rq); 48.#endif 49.}; 50. /* 用于负载均衡* / 51.struct load_weight { 52. unsigned long weight, inv_weight; 53.};
就绪队列
核心调度器用于管理活动进程的主要数据结构称之为就绪队列。每个CPU都有自己的就绪队列,一个进程不可能出现在多个就绪队列,即不能多个CPU不能运行一个进程。
就绪队列的结构如下所示。
kernel/sched.c struct rq { unsigned long nr_running; #define CPU_LOAD_IDX_MAX 5 unsigned long cpu_load[CPU_LOAD_IDX_MAX]; ...... struct load_weight load; struct cfs_rq cfs; struct rt_rq rt; struct task_struct *curr, *idle; u64 clock; ...... }; nr_running:制定了队列上可运行进程的数目。 load:提供了就绪队列当前负荷的度量。 cpu_load:跟踪当前的负荷状态。 cfs和rt:嵌入的子就绪队列,分别用于完全公平调度器和实时调度器。 curr:指向当前运行的进程实例。 idle:指向idle进程的task_struct实例。 clock和pre_raw_clock:用于实现就绪队列自身的时钟。
调度实体
调度器的操作实体结构如下:
<sched.h> struct sched_entity { struct load_weight load; /* 用于负载均衡 */ struct rb_node run_node; unsigned int on_rq; u64 exec_start; u64 sum_exec_runtime; u64 vruntime; u64 prev_sum_exec_runtime; ...... } load:指定了权重,决定了各个实体占队列总负荷的比例。 run_node:标准的树结点,使得实体可以在红黑树上排序。 on_rq:表示该实体当前是否在就绪队列上接受调度。 exec_start:每次调用时,会计算当前时间和exec_start之间的差值,exec则更新到当前时间,差值则被加到sun_exec_time上。 vruntime:统计在进程执行期间虚拟时钟上流逝的时间数量。 pre_sun_exec_runtime:保存进程被撤销时的值。
相关文章推荐
- 内核源码阅读(三)进程命名空间和用户命名的实现
- 内核源码阅读(三)进程命名空间和用户命名的实现
- 内核源码阅读(五)进程ID
- 内核源码阅读(九)进程优先级处理
- 内核源码阅读(四)进程ID
- 内核源码阅读(二)进程复制
- 01 storm 源码阅读 storm的进程间消息通信实现netty server实现
- 02 storm 源码阅读 storm的进程间消息通信实现netty client实现
- 03 storm 源码阅读 storm的进程间消息通信实现clojure端 加载java端netty能力
- 04storm 源码阅读 storm的进程间消息通信实现clojure端 生成带netty能力的线程
- 每日阅读3之内核设计与实现(第三版)4.5——linux调度实现之进程选择
- 【Linxu内核设计与实现】-第3章 进程管理
- Linux-0.11内核源码分析系列:进程调度sleep_on()函数分析
- Dubbo源码分析(六):Dubbo内核实现之动态编译
- 一个简单进程调度器的实现和分析
- 【转】Linux内核进程调度以及定时器实现机制
- Linux用户抢占和内核抢占详解(概念, 实现和触发时机)--Linux进程的管理与调度(二十)
- 【Linxu内核设计与实现】-第4章 进程调度
- UNIX V6内核源码剖析——进程
- OpenCV源码阅读(2)---matx.h---函数的内联实现