其他分享
首页 > 其他分享> > CPU调度子系统

CPU调度子系统

作者:互联网

# CPU调度子系统

- [CPU调度子系统](#cpu调度子系统)

  - [CPU调度系统](#cpu调度系统)

  - [调度系统的机制](#调度系统的机制)

  - [调度系统的策略](#调度系统的策略)

  - [参考链接](#参考链接)

研究调度系统,主要想搞明白两个问题:CFS的调度是如何防御恶意进程的;补充进程调度的相关知识,进而为搞明白编译选项做基础。

## CPU调度系统

1. 调度系统的诞生

   - 调度是为了给用户一个程序并行执行的错觉,让用户的使用更加友好

   - 随着用户的需求的逐渐演变,我们有了不同调度系统

2. 调度系统的两个要求(评价指标)

   - 不同的任务不能互相干扰

     - 这个是调度的基本要求,也是必须要满足的,被称为上下文切换,很考验的是内存管理部分的内容

   - 需要满足不同的调度效率和响应时间的需求,在考虑效率和时间的同时,还需要考虑调度的公平性

     - 调度是调度效率和响应时间的一个权衡,公平性是在地位相同的进程间考虑的,效率和时间是调度的主要评价指标

     - 如果想要调度效率最高,那么就是批处理

       - 这个的使用场景大多是超算一类的机器,对机器的效率有很高的要求

     - 如果想要提升响应时间但是还想要尽可能的保证效率,那么就是我们目前经常用的操作系统

       - 保证响应时间是为了让用户得到更好的使用体验

       - 保证效率是为了让服务器性能得到更好的发挥

     - 如果想要以响应时间为最重要的指标,那么就是实时操作系统,需要通过抢占的方式来提升实时性,保证任务及时完成

       - 硬实时操作系统可以保证任务在规定的时间内完成

       - 软实时操作系统只要按照任务的优先级完成就可以,不保证任务完成的时间

       - 使用的场景大多是工业机器

3. 调度系统的组成部分

   - 为了完成第一个要求,有一个机制来管理进程,并进行上下文切换

     - 机制是为了让进程可以完成上下文切换,将CPU虚拟化

     - 我们这个系列主要是完成策略

   - 为了完成第二个调度的要求,需要有一个策略模块

     - 策略是调度系统的灵魂

## 调度系统的机制

1. 进程的分类

   - 根据进程的不同的要求,我们将其分为不同的种类,进而在后面的调度策略时,尽量满足不同进程的(这里和课本上讲的泾渭分明的调度策略不同,我们可以将不同的要求在同一个电脑上尽可能满足)

   - 硬实时进程

     - 需要在一定时间内完成的进程

   - 软实时进程

     - 需要快速得到结果的进程

   - 普通进程

     - 普通进程也有一定的优先级,比如在PC上,用来给用户以反馈的进程优先级高,计算繁重的进程优先级较低

2. 进程的生命周期

   - 进程的状态

     - 运行

       - 正在CPU上跑

     - 等待

       - 在队列里面等着放在CPU上跑

     - 睡眠

       - 等待外部的事件完成,进而放在等待队列,能CPU上跑

     - 终止

       - 进程结束,资源释放(如果资源已经释放,但是进程任然在进程表中,我们称之为僵尸进程)

   - 进程状态之间的转换

    ![状态转换](https://raw.githubusercontent.com/Richardhongyu/pic/main/20210814195728.png)

   - 进程的权限

     - 我们经常说操作系统处于用户态和核心态,就是指在当前运行的进程时在用户态还是核心态,这两者代表了不同的权限

     - 用户态

       - 此时进程只能访问自身的数据,无法干扰系统中的其他应用程序,甚至也不会注意到自身之外其他程序的存在

     - 核心态

       - 如果进程想要访问系统数据或功能(后者管理着所有进程之间共享的资源,例如文件系统空间),则需要在核心态

     - 从用户态到核心态的切换只能通过两个方式

       - 系统调用

       - 中断

         - 当中断来临时自动切换

         - 中断可以是内核在运行过程中产生的,也可以是外部产生的,注意这里中断和异常在含义上不做区分

         - 中断根据中断的来源分为硬中断(外部)和软中断(内部)

           - 硬中断是电路产生的

           - 软中断是执行程序产生的

3. 进程的表示和机制实现

   - 表示(数据结构)

     - 数据结构task_stuct,这个结构的[代码](https://elixir.bootlin.com/linux/v5.10/source/include/linux/sched.h#L640)直到1366行

     - 这个数据结构包含的内容及其多,我们只能从一些主要的方面入手解读这个结构

     - 进程状态

     - 执行信息

     - 进程身份

     - 资源情况

   - 机制

     - fork

       - 生成一个子进程

     - exec

       - 加载另一个进程,来替代当前的进程,一般是和fork配合,先fork,再在子进程里面exec

     - 上下文切换

## 调度系统的策略

1. 调度器基础

   - 在这个部分,我主要参考了[1-4]

   - 结构

        ![调度器结构](https://raw.githubusercontent.com/Richardhongyu/pic/main/20210814210740.png)

     - 主调度器和周期性调度器合称为通用调度器或者核心调度器,它是一个分配器,通过和调度器类交互选择进程,和调度机制交互完成上下文切换

     - 主调度器和周期性调度器都会和各个调度器类,调度机制交互

   - 调度器调度时机

     - 进程因为睡眠或者IO等原因主动放弃CPU。主调度器就是在这个时机来介入

     - 通过周期性机制,判断是否有必要选择进程。周期性调度器周期性运作,来保证系统不会被某个程序一直占用

     - 也就是主调度器或者周期性调度器适时介入,然后再通过调度类判断下一个执行的进程,最后通过调度器机制,完成上下文切换等一系列调度工作

   - 主调度器

     - 相关函数

       - schedule

     - 主调度器的任务

       - 在内核中的许多地方,如果要将CPU分配给与当前活动进程不同的另一个进程,都会直接调用主调度器函数(schedule)。

       - 在从系统调用返回之后,内核也会检查当前进程是否设置了重调度标志TIF_NEED_RESCHED

     - 调度流程

        ![调度流程](https://raw.githubusercontent.com/Richardhongyu/pic/main/640)

        - 相当多的工作是交给调度器类完成的

   - 周期性调度器

     - 相关函数

       - scheduler_tick

     - 周期性调度器的任务

       - 管理内核中与整个系统和各个进程的调度相关的统计量。其间执行的主要操作是对各种计数器加1

       - 激活负责当前进程的调度类的周期性调度方法。

     - 调度流程

        ![调度流程](https://raw.githubusercontent.com/Richardhongyu/pic/main/20210815122254.png)

        - 通过函数指针,调用调度器类里面实现的task_tick,让调度器类实现相应的工作

        - 如果当前的进程需要被调度,那么调度器类方法会在task_struct中设置TIF_NEED_RESCHED标志,以表示该请求,而内核会在接下来的适当时机完成该请求,本质还是传递给主调度器完成。

   - 调度器类

        ![调度器类](https://raw.githubusercontent.com/Richardhongyu/pic/main/20210814211853.png)

        - 调度器类结构

            ```shell

            struct sched_class { 

                const struct sched_class *next; 

                void (*enqueue_task) (struct rq *rq, struct task_struct *p, int wakeup); 

                void (*dequeue_task) (struct rq *rq, struct task_struct *p, int sleep); 

                void (*yield_task) (struct rq *rq); 

                void (*check_preempt_curr) (struct rq *rq, struct task_struct *p); 

                struct task_struct * (*pick_next_task) (struct rq *rq); 

                void (*put_prev_task) (struct rq *rq, struct task_struct *p); 

                void (*set_curr_task) (struct rq *rq); 

                void (*task_tick) (struct rq *rq, struct task_struct *p); 

                void (*task_new) (struct rq *rq, struct task_struct *p); 

            }; 

            ```

        - 调度器类提供了通用调度器和调度策略之间的关系

        - 实时进程调度器类在最前面,然后是普通进程调度器类,最后是空闲进程调度器类

        - 调度器类在被通用调度器中的周期性调度器调用时,task_tick是其执行的函数

        - 这里充分体现了包括了函数指针的妙用

   - 进程调度策略

     - 我们给每个进程一个调度策略,表明我们希望如何调度这个进程,以在一个系统内,满足不同的要求

        ![调度策略](https://raw.githubusercontent.com/Richardhongyu/pic/main/20210814212256.png)

     - SCHED_NORMAL调度普通进程,这也是我们日常使用最多的进程

     - SCHED_BATCH调度批处理进程

     - SCHED_FIFO和SCHED_RR和SCHED_DEADLINE则采用不同的调度策略调度实时进程,

     - SCHED_IDLE则在系统空闲时调用idle进程

     - 不同的调度进程

   - 调度实体

     - 调度器不限于调度进程,还可以调度更大的实体。CPU时间一般用于组调度,首先在进程组之间分配,然在组内分配。

        ```shell

        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; 

        ... 

        } 

        ```

   - 调度优先级

     - 优先级的计算

       - 虚拟时间

     - 优先级的实现

       - 主调度器

       - 周期性调度器

2. CFS调度类

   - 这一小节的内容主要参考了[1-3],由于之后并不会用到这里的内容,这里用[3]中的几个流程图来描述这一小节的内容

   - 首先来看调度类是如何实现的

        ![调度类](https://raw.githubusercontent.com/Richardhongyu/pic/main/20210815115808.png)

   - 然后看解决出队和入队的流程

        ![出入队](https://raw.githubusercontent.com/Richardhongyu/pic/main/20210815120144.png)

   - 接着看如何挑选下一个进程

        ![挑选进程](https://raw.githubusercontent.com/Richardhongyu/pic/main/20210815120558.png)

   - 任务创建

        ![任务创建](https://raw.githubusercontent.com/Richardhongyu/pic/main/20210815120620.png)

3. 实时调度类

   - 和优化CPU统计时间的主题关系不是太大,这里先略过

## 参考链接

[1] https://blog.csdn.net/gatieme/article/details/51699889  

[2] 《深入理解Linux架构》  

[3] https://mp.weixin.qq.com/s/6as3ZB5s-H9jKtxAorGIfg  

[4] https://mp.weixin.qq.com/s/BOmuGLb1iQ57lO_nnAn2xA

标签:task,rq,调度,子系统,https,进程,CPU,struct
来源: https://blog.csdn.net/weixin_41070748/article/details/120142080