mirror of
https://github.com/gatieme/LDD-LinuxDeviceDrivers.git
synced 2026-11-09 08:38:04 +08:00
进程调度之进程上下文切换详解...
This commit is contained in:
1 parent
69c8e5539e
commit
e6f778bc78
22 files changed
+184
No files matched your search
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+4
@@ -868,3 +868,7 @@ static struct rq *finish_task_switch(struct task_struct *prev)
|
||||
```
|
||||
|
||||
|
||||
#4 linux下进程切换性能分析
|
||||
-------
|
||||
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,136 @@
|
||||
Linux进程调度器的设计
|
||||
=======
|
||||
|
||||
|
||||
| 日期 | 内核版本 | 架构| 作者 | GitHub| CSDN |
|
||||
| ------- |:-------:|:-------:|:-------:|:-------:|:-------:|
|
||||
| 2016-06-14 | [Linux-4.6](http://lxr.free-electrons.com/source/?v=4.6) | X86 & arm | [gatieme](http://blog.csdn.net/gatieme) | [LinuxDeviceDrivers](https://github.com/gatieme/LDD-LinuxDeviceDrivers) | [Linux进程管理与调度](http://blog.csdn.net/gatieme/article/category/6225543) |
|
||||
|
||||
|
||||
#前景回顾
|
||||
-------
|
||||
|
||||
|
||||
##进程调度
|
||||
-------
|
||||
|
||||
linux把进程区分为实时进程和非实时进程, 其中非实时进程进一步划分为交互式进程和批处理进程
|
||||
|
||||
|
||||
|
||||
根据进程的不同分类Linux采用不同的调度策略.
|
||||
|
||||
对于实时进程,采用FIFO或者Round Robin的调度策略.
|
||||
|
||||
对于普通进程,则采用CFS完全功能公平调度器
|
||||
|
||||
##linux调度器的演变
|
||||
-------
|
||||
|
||||
一开始的调度器是复杂度为**$O(n)$的始调度算法**(实际上每次会遍历所有任务,所以复杂度为O(n)), 这个算法的缺点是当内核中有很多任务时,调度器本身就会耗费不少时间,所以,从linux2.5开始引入赫赫有名的**$O(1)$调度器**
|
||||
|
||||
|
||||
|
||||
| 字段 | 版本 |
|
||||
| ------------- |:-------------:|
|
||||
| O(n)的始调度算法 | linux-0.11~2.4 |
|
||||
| O(1)调度器 | linux-2.5 |
|
||||
| CFS调度器 | linux-2.6~至今 |
|
||||
|
||||
|
||||
#Linux的调度器组成
|
||||
-------
|
||||
|
||||
|
||||
##2个调度器
|
||||
-------
|
||||
|
||||
可以用两种方法来激活调度
|
||||
|
||||
* 一种是直接的, 比如进程打算睡眠或出于其他原因放弃CPU
|
||||
|
||||
* 另一种是通过周期性的机制, 以固定的频率运行, 不时的检测是否有必要
|
||||
|
||||
因此当前linux的调度程序由两个调度器组成:**主调度器**,**周期性调度器**(两者又统称为**通用调度器(generic scheduler)**或**核心调度器(core scheduler)**)
|
||||
|
||||
并且每个调度器包括两个内容:**调度框架**(其实质就是两个函数框架)及**调度器类**
|
||||
|
||||
|
||||
|
||||
##6种调度策略
|
||||
-------
|
||||
|
||||
|
||||
linux内核目前实现了6中调度策略(即调度算法), 用于对不同类型的进程进行调度, 或者支持某些特殊的功能
|
||||
|
||||
比如SCHED_NORMAL和SCHED_BATCH调度普通的非实时进程, SCHED_FIFO和SCHED_RR和SCHED_DEADLINE则采用不同的调度策略调度实时进程, SCHED_IDLE则在系统空闲时调用idle进程.
|
||||
|
||||
|
||||
|
||||
|
||||
##5个调度器类
|
||||
-------
|
||||
|
||||
而依据其调度策略的不同实现了5个调度器类, 一个调度器类可以用一种种或者多种调度策略调度某一类进程, 也可以用于特殊情况或者调度特殊功能的进程.
|
||||
|
||||
|
||||
|
||||
其所属进程的优先级顺序为
|
||||
````c
|
||||
stop_sched_class -> dl_sched_class -> rt_sched_class -> fair_sched_class -> idle_sched_class
|
||||
```
|
||||
|
||||
##3个调度实体
|
||||
-------
|
||||
|
||||
调度器不限于调度进程, 还可以调度更大的实体, 比如实现组调度: 可用的CPUI时间首先在一半的进程组(比如, 所有进程按照所有者分组)之间分配, 接下来分配的时间再在组内进行二次分配.
|
||||
|
||||
这种一般性要求调度器不直接操作进程, 而是处理可调度实体, 因此需要一个通用的数据结构描述这个调度实体,即seched_entity结构, 其实际上就代表了一个调度对象,可以为一个进程,也可以为一个进程组.
|
||||
|
||||
linux中针对当前可调度的实时和非实时进程, 定义了类型为seched_entity的3个调度实体
|
||||
|
||||
|
||||
* sched_dl_entity 采用EDF算法调度的DEADLINE实时调度实体
|
||||
|
||||
* sched_rt_entity 采用Roound-Robin或者FIFO算法调度的实时调度实体
|
||||
|
||||
* sched_entity 采用CFS算法调度的普通非实时进程的调度实体
|
||||
|
||||
|
||||
每个进程都属于某个调度器类(由字段task_struct->sched_class标识), 由调度器类采用进程对应的调度策略调度(由task_struct->policy )进行调度, task_struct也存储了其对应的调度实体标识
|
||||
|
||||
linux实现了6种调度策略, 依据其调度策略的不同实现了5个调度器类, 一个调度器类可以用一种或者多种调度策略调度某一类进程, 也可以用于特殊情况或者调度特殊功能的进程.
|
||||
|
||||
|
||||
调度器类, 调度策略, 调度实体他们之间的关系如下表
|
||||
|
||||
| 调度器类 | 调度策略 | 调度策略对应的调度算法 | 调度实体 | 调度实体对应的调度对象 |
|
||||
| ------- |:-------:|:-------:|:-------:||:-------:|
|
||||
| stop_sched_class | 无 | 无 | 无 | 特殊情况, 发生在cpu_stop_cpu_callback 进行cpu之间任务迁移migration或者HOTPLUG_CPU的情况下关闭任务 |
|
||||
| dl_sched_class | SCHED_DEADLINE | Earliest-Deadline-First最早截至时间有限算法 | sched_dl_entity | 采用DEF最早截至时间有限算法调度实时进程 |
|
||||
| rt_sched_class | SCHED_RR<br><br>SCHED_FIFO | Roound-Robin时间片轮转算法<br><br>FIFO先进先出算法 | sched_rt_entity | 采用Roound-Robin或者FIFO算法调度的实时调度实体 |
|
||||
| fair_sched_class | SCHED_NORMAL<br><br>SCHED_BATCH | CFS完全公平懂调度算法 |sched_entity | 采用CFS算法普通非实时进程 |
|
||||
| idle_sched_class | SCHED_IDLE | 无 | 无 | 特殊进程, 用于cpu空闲时调度空闲进程idle |
|
||||
|
||||
#cfs完全公平调度器
|
||||
-------
|
||||
|
||||
##CFS调度器类fair_sched_class
|
||||
-------
|
||||
|
||||
CFS完全公平调度器的调度器类叫fair_sched_class, 其定义在[kernel/sched/fair.c, line 8521](http://lxr.free-electrons.com/source/kernel/sched/fair.c#L8521), 它是我们熟知的是struct sched_class调度器类类型, 将我们的CFS调度器与一些特定的函数关联起来
|
||||
|
||||
|
||||
|
||||
| sched_class字段 | fair_sched_class函数| 描述 |
|
||||
| ------------- |:-------------:|
|
||||
| load | 指定了权重, 决定了各个实体占队列总负荷的比重, 计算负荷权重是调度器的一项重任, 因为CFS所需的虚拟时钟的速度最终依赖于负荷, 权重通过优先级转换而成,是vruntime计算的关键 |
|
||||
| run_node | 调度实体在红黑树对应的结点信息, 使得调度实体可以在红黑树上排序 |
|
||||
| sum_exec_runtime | 记录程序运行所消耗的CPU时间, 以用于完全公平调度器CFS |
|
||||
| on_rq | 调度实体是否在就绪队列上接受检查, 表明是否处于CFS红黑树运行队列中,需要明确一个观点就是,CFS运行队列里面包含有一个红黑树,但这个红黑树并不是CFS运行队列的全部,因为红黑树仅仅是用于选择出下一个调度程序的算法。很简单的一个例子,普通程序运行时,其并不在红黑树中,但是还是处于CFS运行队列中,其on_rq为真。只有准备退出、即将睡眠等待和转为实时进程的进程其CFS运行队列的on_rq为假 |
|
||||
| vruntime | 虚拟运行时间,调度的关键,其计算公式:一次调度间隔的虚拟运行时间 = 实际运行时间 * (NICE_0_LOAD / 权重)。可以看出跟实际运行时间和权重有关,红黑树就是以此作为排序的标准,优先级越高的进程在运行时其vruntime增长的越慢,其可运行时间相对就长,而且也越有可能处于红黑树的最左结点,调度器每次都选择最左边的结点为下一个调度进程。注意其值为单调递增,在每个调度器的时钟中断时当前进程的虚拟运行时间都会累加。单纯的说就是进程们都在比谁的vruntime最小,最小的将被调度 |
|
||||
| cfs_rq | 此调度实体所处于的CFS运行队列 |
|
||||
| my_q | 如果此调度实体代表的是一个进程组,那么此调度实体就包含有一个自己的CFS运行队列,其CFS运行队列中存放的是此进程组中的进程,这些进程就不会在其他CFS运行队列的红黑树中被包含(包括顶层红黑树也不会包含他们,他们只属于这个进程组的红黑树) |
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
8518 /*
|
||||
8519 * All the scheduling class methods:
|
||||
8520 */
|
||||
8521 const struct sched_class fair_sched_class = {
|
||||
8522 .next = &idle_sched_class,
|
||||
8523 .enqueue_task = enqueue_task_fair,
|
||||
8524 .dequeue_task = dequeue_task_fair,
|
||||
8525 .yield_task = yield_task_fair,
|
||||
8526 .yield_to_task = yield_to_task_fair,
|
||||
8527
|
||||
8528 .check_preempt_curr = check_preempt_wakeup,
|
||||
8529
|
||||
8530 .pick_next_task = pick_next_task_fair,
|
||||
8531 .put_prev_task = put_prev_task_fair,
|
||||
8532
|
||||
8533 #ifdef CONFIG_SMP
|
||||
8534 .select_task_rq = select_task_rq_fair,
|
||||
8535 .migrate_task_rq = migrate_task_rq_fair,
|
||||
8536
|
||||
8537 .rq_online = rq_online_fair,
|
||||
8538 .rq_offline = rq_offline_fair,
|
||||
8539
|
||||
8540 .task_waking = task_waking_fair,
|
||||
8541 .task_dead = task_dead_fair,
|
||||
8542 .set_cpus_allowed = set_cpus_allowed_common,
|
||||
8543 #endif
|
||||
8544
|
||||
8545 .set_curr_task = set_curr_task_fair,
|
||||
8546 .task_tick = task_tick_fair,
|
||||
8547 .task_fork = task_fork_fair,
|
||||
8548
|
||||
8549 .prio_changed = prio_changed_fair,
|
||||
8550 .switched_from = switched_from_fair,
|
||||
8551 .switched_to = switched_to_fair,
|
||||
8552
|
||||
8553 .get_rr_interval = get_rr_interval_fair,
|
||||
8554
|
||||
8555 .update_curr = update_curr_fair,
|
||||
8556
|
||||
8557 #ifdef CONFIG_FAIR_GROUP_SCHED
|
||||
8558 .task_move_group = task_move_group_fair,
|
||||
8559 #endif
|
||||
8560 };
|
||||
File renamed without changes.
@@ -14,6 +14,7 @@ Linux进程退出详解
|
||||
6. 优先级priority
|
||||
7. 抢占-内核抢占和用户抢占以及抢占的时机preempt
|
||||
8. 进程切换context_switch
|
||||
|
||||
9. 完全公平调度器cfs-设计
|
||||
10. cfs-负荷权重
|
||||
11. cfs-虚拟时钟
|
||||
|
||||
Reference in new issue
Block a user