kernel开发3:CPU调度子系统

CPU调度子系统

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

CPU调度系统

  1. 调度系统的诞生

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

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

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

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

      • 这个是调度的基本要求,也是必须要满足的,被称为上下文切换,很考验的是内存管理部分的内容
    • 需要满足不同的调度效率和响应时间的需求,在考虑效率和时间的同时,还需要考虑调度的公平性

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

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

        • 这个的使用场景大多是超算一类的机器,对机器的效率有很高的要求
      • 如果想要提升响应时间但是还想要尽可能的保证效率,那么就是我们目前经常用的操作系统

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

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

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

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

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

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

  3. 调度系统的组成部分

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

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

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

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

      • 策略是调度系统的灵魂

调度系统的机制

  1. 进程的分类

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

    • 硬实时进程

      • 需要在一定时间内完成的进程
    • 软实时进程

      • 需要快速得到结果的进程
    • 普通进程

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

    • 进程的状态

      • 运行

        • 正在CPU上跑
      • 等待

        • 在队列里面等着放在CPU上跑
      • 睡眠

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

        • 进程结束,资源释放(如果资源已经释放,但是进程任然在进程表中,我们称之为僵尸进程)
    • 进程状态之间的转换

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i58UZ1MZ-1630926102188)(https://raw.githubusercontent.com/Richardhongyu/pic/main/20210814195728.png)]

    • 进程的权限

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

      • 用户态

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

        • 如果进程想要访问系统数据或功能(后者管理着所有进程之间共享的资源,例如文件系统空间),则需要在核心态
      • 从用户态到核心态的切换只能通过两个方式

        • 系统调用

        • 中断

          • 当中断来临时自动切换

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

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

            • 硬中断是电路产生的

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

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

    • 表示(数据结构)

      • 数据结构task_stuct,这个结构的代码直到1366行

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

      • 进程状态

      • 执行信息

      • 进程身份

      • 资源情况

    • 机制

      • fork

        • 生成一个子进程
      • exec

        • 加载另一个进程,来替代当前的进程,一般是和fork配合,先fork,再在子进程里面exec
      • 上下文切换

调度系统的策略

  1. 调度器基础

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

    • 结构

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ghcQMQi6-1630926102197)(https://raw.githubusercontent.com/Richardhongyu/pic/main/20210814210740.png)]

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

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

    • 调度器调度时机

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

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

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

    • 主调度器

      • 相关函数

        • schedule
      • 主调度器的任务

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

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

      • 调度流程

        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cSID3pBQ-1630926102200)(https://raw.githubusercontent.com/Richardhongyu/pic/main/640)]

        • 相当多的工作是交给调度器类完成的
    • 周期性调度器

      • 相关函数

        • scheduler_tick
      • 周期性调度器的任务

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

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

      • 调度流程

        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yu7bswRJ-1630926102204)(https://raw.githubusercontent.com/Richardhongyu/pic/main/20210815122254.png)]

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

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

    • 调度器类

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rJoG8LwQ-1630926102208)(https://raw.githubusercontent.com/Richardhongyu/pic/main/20210814211853.png)]

      • 调度器类结构

        
        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是其执行的函数

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

    • 进程调度策略

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

        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0BM8FUnj-1630926102210)(https://raw.githubusercontent.com/Richardhongyu/pic/main/20210814212256.png)]

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

      • SCHED_BATCH调度批处理进程

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

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

      • 不同的调度进程

    • 调度实体

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

        
        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]中的几个流程图来描述这一小节的内容

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

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xPvKblzC-1630926102215)(https://raw.githubusercontent.com/Richardhongyu/pic/main/20210815115808.png)]

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

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CbIJZp7L-1630926102217)(https://raw.githubusercontent.com/Richardhongyu/pic/main/20210815120144.png)]

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

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Tj7uHhd8-1630926102219)(https://raw.githubusercontent.com/Richardhongyu/pic/main/20210815120558.png)]

    • 任务创建

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mweVCFZz-1630926102221)(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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值