• Linux内核进程调度overview(1)


    一、概述

      决定何时、如何选择一个新进程运行的这组规则叫做:调度策略(scheduling policy)。

      Linux的调度是基于分时技术(time sharing):多个进程以“时间多路复用”方式运行,因为CPU的时间呗分成“片”(slice),给每个可运行进程分配一片。如果当前运行进程的时间片或时限(quantum)到期时,该进程还没有运行完毕,进程切换就会发生。

      调度策略也是根据进程的优先级对它们进行分类。在Linux中,进程优先级是动态的:在较长时间内没有运行的进程,会动态提升它们的优先级;相反地,对于在CPU上运行较长时间的进程,会降低它们的优先级来惩罚它们。

      所以,实现调度的工具是调度器(scheduler),调度的对象是进程(process),调度的方法是调度策略(包括调度算法)。

    二、CPU调度器 

     

     这里主要讲了cpu调度器的工作内容和目的:

    1. 多个task会共享CPU资源
    2. 那如何进行任务切换选择呢?
      • 当前运行的task中止
      • 当前运行的task sleep(wait event)
      • 新task创建,或者sleep的task唤醒了
      • 当前运行的task的时间片用完

    那调度器的目标是什么?

    1. 公平调度各个task
    2. 基于task的优先级来分配时间片
    3. task的respnse时间短
    4. 高throughput(task执行成功)
    5. 在多个cpu间,负载均衡
    6. 低功耗
    7. 调度器代码运行开销低

    调度器会和工作在一些框架、服务器、PC、嵌入式/手机中。

    三、O(1)调度器

    在2.6.23(2007)以前,Linux调度器使用的是O(1)调度器:

    • 调度器分140个优先级等级:0-99是RT task,100-139是User task
    • 每个cpu的runqueue有2个数组:Active,Expaired
    • 每个数组都有140个entry,对应每个优先级
    • 每个entry是一条FIFO队列结构的链表
    • 140位的bitmap用来检测每个优先级list
    • 时间片会根据task的优先级进行分配
    • 运行时间expaire的task会从Active数组移动到Expaired数组
    • 当Active数组为空时,就交换2个数组。即,将Expaired数组变为Active;Active(此时为空)变为Expaired
    • task的入rq和出rq,以及next task的选择都是在固定时间内完成

    最后在O(1)调度器已经被CFS替代。

    四、当前调度器架构

    • 在kernel 2.6.23(2007)后,由Ingo Molnar引入
    • 在调度的class中,还存在调度policy
    • 不同的调度class,高优先级的,越早执行
    • task可以在cpu、调度policy、调度class间进行迁移

    4.1  调度class

    由struce sched_class结构体实现:

    struct sched_class {
        const struct sched_class *next;
        void (*enqueue_task) (struct rq *rq, struct task_struct *p, int flags);
        void (*dequeue_task) (struct rq *rq, struct task_struct *p, int flags);
    ...
        struct task_struct * (*pick_next_task) (struct rq *rq, struct task_struct *prev, struct rq_flags *rf);
    ...
    };

    内核中一共有5中调度class,优先级从高到低:STOP > DL > RT > CFS > IDLE,他们通过链表实现,并链接起来的。

     4.2  主调度函数Schedule()

    内核中进程调度,最主要的实现是Schedule()函数。它完成如下工作:

    • 选取下一个runnable的task,并将task放在cpu上运行
    • 按class优先级搜索task来运行,最先从STOP class开始
    • 轮询搜索:for_each_class()
    • 实现方式:pick_next_task():
    again:
        for_each_class(class) {
            p = class->pick_next_task(rq, prev, rf);
            if (p) {
                if (unlikely(p == RETRY_TASK))
                    goto again;
                return p;
            }
        }
    
        /* The idle class should always have a runnable task: */
        BUG();

    4.3  调度class与policy

    在不同的调度class下,可能会有不同的调度policy实现:

    ● Stop
      ○ No policy
    ● Deadline
      ○ SCHED_DEADLINE
    ● Real Time
      ○ SCHED_FIFO
      ○ SCHED_RR
    ● Fair
      ○ SCHED_NORMAL
      ○ SCHED_BATCH
      ○ SCHED_IDLE
    ● Idle
      ○ No policy

    不同的class代表不同的调度优先级;不同的policy同样也意味着不同的调度方式。

    4.4  调度class:STOP

    STOP类型的class有如下特点:

    • 是最高优先级的class(但是这个class不开放给系统user使用的)
    • 只能在smp系统上可用(stop_machine()在单核处理器下不可用)------括号内的具体没怎么理解
    • 可以抢占所有task,并任何事件都不能抢占它
    • 实现方式是:停止运行的其他所有task,而在cpu上运行一个特定的函数
    • 没有调度policy
    • 属于stop class的per cpu内核线程:migration/N ------“N”为cpu core number
    • 在以下情况下使用:task迁移、CPU hotplug、RCU、ftrace、cloclevents等

    4.5  调度class:Deadline(DL)

    Deadline类型的class有如下特点:

    • 在kernel 3.14(2013),由Dario Faggioli & Juri Lelli引入
    • 在系统中,属于可以使用的最高优先级的class
    • 调度policy为SCHED_DEADLINE
    • 由红黑树结构实现(自平衡树)
    • 在以下情况下使用:周期性的实时task,例如:视频编解码

    4.6  调度Real-time(RT)

    Real-time类型的class有如下特点:

    • 符合POSIX标准要求
    • task优先级范围:0-99
    • 优先级在kernel和userspace中相反:0在kernel中,代表最高优先级;而在userspace中代表最低优先级
    • 相同优先级下的调度policy:
      • SCHED_FIFO
      • SCHED_RR,默认时间片长度为100ms
    • 由链表实现
    • 在以下情况下使用:latency敏感的task,例如:IRQ threads

    4.7  调度CFS(Completely Fair Scheduler)

    CFS类型的class有如下特点:

    • 由Ingo Molnar引入
    • 调度policy:
      • SCHED_NORMAL:普通task
      • SCHED_BATCH:批处理 task(batch task,非交互型)
      • SCHED_IDLE:低优先级task
    • 由红黑树结构实现
    • 跟踪task的虚拟运行时间(vruntime,task拥有的运行时间)
    • 虚拟运行时间(vruntime)最短的task,最优先运行
    • task的优先级作为权重,会影响虚拟运行时间的计算(vruntime)
    • 权重越大,虚拟运行时间(vruntime)计算时的增量就越小
    • task的优先级计算:120+nice值(nice范围:-20 ~ +19)
    • 用于所有其他类型的task,例如:shell

    4.7  调度Idle

    Idle类型的class有如下特点:

    • 最低优先级的调度class
    • 没有调度policy
    • 属于idle class的per cpu内核线程(idle):swapper/N ------“N”为cpu core number
    • idle线程仅会在没有其他task的情况下,在cpu上运行
    • idle线程可以让cpu进入低功耗状态

    五、Runqueue

    • 每个CPU都由一个struct rq的实例
    • 每个”rq“包含了DL、RT、CFS的runqueue
    • Runnable的task会被压入上面提到的那些runqueue中
    • 在struct rq中由很多其他的信息和状态
    struct rq {
    ...
        struct cfs_rq cfs;
        struct rt_rq rt;
        struct dl_rq dl;
    ...
    }

     

  • 相关阅读:
    git subtree用法
    Excel导入、导出库:ExcelKit
    [C#.NET 拾遗补漏]08:强大的LINQ
    使用.net standard实现不同内网端口的互通(类似花生壳)
    LINQ:最终统治了所有的语言!
    浅谈代码段加密原理(防止静态分析)
    HashTable、HashSet和Dictionary的区别(转)
    Mysql分表和分区的区别、分库分表介绍与区别
    划词高亮功能的实现附带开源代码
    十个推荐使用的 Laravel 的辅助函数
  • 原文地址:https://www.cnblogs.com/lingjiajun/p/10369521.html
Copyright © 2020-2023  润新知