Skip to content

Commit

Permalink
docs(sched):调度子系统文档即cfs文档 (#807)
Browse files Browse the repository at this point in the history
* 调度子系统文档以及cfs文档
  • Loading branch information
GnoCiYeH authored May 7, 2024
1 parent 0102d69 commit b941261
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 21 deletions.
30 changes: 17 additions & 13 deletions docs/kernel/sched/cfs.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,27 @@

   CFS(Completely Fair Scheduler),顾名思义,完全公平调度器。CFS作为主线调度器之一,也是最典型的O(1)调度器之一

## 1. CFSQueue 介绍
## 结构体介绍

   CFSQueue是用来存放普通进程的调度队列,每个CPU维护一个CFSQueue,主要使用Vec作为主要存储结构来实现。
- ``CompletelyFairScheduler``
   ``CompletelyFairScheduler``实现了``Scheduler``trait,他是完全调度算法逻辑的主要实施者。

### 1.1 主要函数
1. enqueue(): 将pcb入队列
2. dequeue(): 将pcb从调度队列中弹出,若队列为空,则返回IDLE进程的pcb
3. sort(): 将进程按照虚拟运行时间的升序进行排列
- ``FairSchedEntity``
- **重要字段**
- ``cfs_rq``: 它指向了自己所在的完全公平调度队列。
- ``my_cfs_rq``: 为一个``Option``变量,当该实体作为一个单独进程时,这个值为``None``,但是若这个实体为一个组,那这个变量必需为这个组内的私有调度队列。这个``cfs_rq``还可以继续往下深入,就构成了上述的树型结构。
- ``pcb``: 它指向了当前实体对应的``PCB``,同样,若当前实体为一个组,则这个``Weak``指针不指向任何值。

## 2. SchedulerCFS 介绍
  ``FairSchedEntity``是完全公平调度器中最重要的结构体,他代表一个实体单位,它不止表示一个进程,它还可以是一个组或者一个用户,但是它在cfs队列中所表示的就单单是一个调度实体。这样的设计可以为上层提供更多的思路,比如上层可以把不同的进程归纳到一个调度实体从而实现组调度等功能而不需要改变调度算法。

   CFS调度器类,主要实现了CFS调度器类的初始化以及调度功能函数。
  在cfs中,整体的结构是**一棵树**,每一个调度实体作为``cfs_rq``中的一个节点,若该调度实体不是单个进程(它可能是一个进程组),则在该调度实体中还需要维护一个自己的``cfs_rq``,这样的嵌套展开后,每一个叶子节点就是一个单独的进程。需要理解这样一棵树,**在后续文档中会以这棵树为核心讲解**
  该结构体具体的字段意义请查阅源代码。这里提及几个重要的字段:

### 2.1 主要函数

1. sched(): 是对于Scheduler trait的sched()实现,是普通进程进行调度时的逻辑处理,该函数会返回接下来要执行的pcb,若没有符合要求的pcb,返回None
2. enqueue(): 同样是对于Scheduler trait的sched()实现,将一个pcb加入调度器的调度队列
3. update_cpu_exec_proc_jiffies(): 更新这个cpu上,这个进程的可执行时间。
4. timer_update_jiffies(): 时钟中断到来时,由sched的core模块中的函数,调用本函数,更新CFS进程的可执行时间
- ``CfsRunQueue``
  ``CfsRunQueue``完全公平调度算法中管理``FairSchedEntity``的队列,它可以挂在总的``CpuRunQueue``下,也可以作为子节点挂在``FairSchedEntity``上,详见上文``FairSchedEntity``

- **重要字段**
- ``entities``: 存储调度实体的红黑树
- ``current``: 当前正在运行的实体

67 changes: 59 additions & 8 deletions docs/kernel/sched/core.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,65 @@
# 进程调度器相关的api

   定义了DragonOS的进程调度相关的api,是系统进行进程调度的接口。同时也抽象出了Scheduler的trait,以供具体的调度器实现
   定义了DragonOS的进程调度相关的api,是系统进行进程调度的接口。同时也抽象出了Scheduler的trait,以供具体的调度器实现

## 1. 调度器介绍
## 调度器介绍

   一般来说,一个系统会同时处理多个请求,但是其资源是优先的,调度就是用来协调每个请求对资源的使用的方法。

### 1.1 主要函数
1. cpu_executing(): 获取指定的cpu上正在执行的进程的pcb
2. sched_enqueue(): 将进程加入调度队列
3. sched_init(): 初始化进程调度器模块
4. sched_update_jiffies(): 当时钟中断到达时,更新时间片。*请注意,该函数只能被时钟中断处理程序调用*
5. sys_sched(): 让系统立即运行调度器的系统调用。*请注意,该系统调用不能由ring3的程序发起*
## 整体架构
  整个调度子系统以**树形结构**来组织,每个CPU都会管理这样一棵树,每个CPU的``CpuRunQueue``即可以理解为树的根节点。每个``CpuRunQueue``下会管理着不同调度策略的子树,根据不同的调度策略深入到对应子树中实施调度。大体结构如下:

- CpuRunQueue
- Cfs
- CfsRunQueue
- FairSchedEntity
- CfsRunQueue
- ...(嵌套)
- Rt
- ...
- Idle
- ...
- RR
- ...
- ...

  基于这个结构,调度子系统能够更轻松地解耦以及添加其他调度策略。
  

## 重要结构
- ``Scheduler:``
  ``Scheduler``是各个调度算法提供给上层的接口,实现不同的调度算法,只需要向外提供这样一组接口即可。

- ``CpuRunQueue:``
  ``CpuRunQueue``为总的CPU运行队列,他会根据不同的调度策略来进行调度。他作为调度子系统的根节点来组织调度。
- **重要字段**
- ``lock``: 过程锁,因为在深入到具体调度策略后的调度过程中还会需要访问``CpuRunQueue``中的信息,在cfs中保存了``CpuRunQueue``对象,我们需要确保在整体过程上锁后,子对象中不需要二次加锁即可访问,所以过程锁比较适合这个场景,若使用对象锁,则在对应调度策略中想要访问``CpuRunQueue``中的信息时需要加锁,但是最外层已经将``CpuRunQueue``对象上锁,会导致内层永远拿不到锁。对于该字段,详见[CpuRunQueue的self_lock方法及其注释](https://code.dragonos.org.cn/xref/DragonOS/kernel/src/sched/mod.rs?r=dd8e74ef0d7f91a141bd217736bef4fe7dc6df3d#360)
- ``cfs``: Cfs调度器的根节点,往下伸展为一棵子树,详见完全公平调度文档。
- ``current``: 当前在CPU上运行的进程。
- ``idle``: 当前CPU的Idle进程。


## 调度流程
  一次有效的调度分两种情况,第一是主动调用``__schedule``或者``schedule``函数进行调度,第二是通过时钟中断,判断当前运行的任务时间是否到期。

- **主动调度**
- ``__schedule``和``schedule``函数:
- ``__schedule``:真正执行调度。会按照当前调度策略来选择下一个任务执行。
- ``schedule``: ``__schedule``的上层封装,它需要该任务在内核中的所有资源释放干净才能进行调度,即判断当前进程的``preempt_count``是否为0,若不为0则会**panic**。
- 参数:这两个函数都需要提供一个参数:``SchedMode``。用于控制此次调度的行为,可选参数主要有以下两个:
- ``SchedMode::SM_NONE``: 标志当前进程没有被抢占而是主动让出,他**不会**被再次加入队列,直到有其他进程主动唤醒它,这个标志位主要用于信号量、等待队列以及一些主动唤醒场景的实现。
- ``SchedMode::SM_PREEMPT``:标志当前是被**抢占**运行的,他**会**再次被加入调度队列等待下次调度,通俗来说:它是被别的进程抢占了运行时间,有机会运行时他会继续执行。

- **时钟调度**
  时钟中断到来的时候,调度系统会进行更新,包括判断是否需要下一次调度。以下为主要的函数调用栈:
- ``LocalApicTimer::handle_irq``: 中断处理函数
- ``ProcessManager::update_process_times``: 更新当前进程的时钟信息(统计运行时等)
- ``scheduler_tick``: 调度子系统tick入口
- ``CompletelyFairScheduler::tick``: 以cfs为例,此为cfs调度算法的tick入口
- ``CfsRunQueue::entity_tick``: 对所有调度实体进行tick
- ``CfsRunQueue::update_current``: 更新当前运行任务的运行时间及判断是否到期
- ``CfsRunQueue::account_cfs_rq_runtime``: 计算当前队列的运行时间
- ``CpuRunQueue::resched_current``: 若上一步计算的时间超时则到这一步,这里会设置进程标志为``NEED_SCHEDULE``.

- 退出中断:退出中断时检查当前进程是否存在标志位``NEED_SCHEDULE``,若存在则调用``__schedule``进行调度。

0 comments on commit b941261

Please sign in to comment.