Zijing Zhang
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # Nerd OS 问题交流 ## 代码树介绍(以riscv为例) ``` |-- arch(架构相关) | |-- aarch64 | | |-- config.rs | | |-- context.rs | | |-- instructions.rs | | |-- mod.rs | | |-- page_table.rs | | |-- percpu.rs | | |-- trap.rs | | `-- trap.S | |-- mod.rs | |-- riscv(riscv架构相关) | | |-- config.rs(配置文件,内核和用户地址空间设置以及sv39页机制) | | |-- context.rs(上下文相关寄存器和处理逻辑) | | |-- instructions.rs | | |-- macros.rs | | |-- mod.rs | | |-- page_table.rs(页机制) | | |-- percpu.rs | | |-- trap.rs(trap逻辑处理) | | `-- trap.S(trap汇编逻辑) | `-- x86_64 | |-- config.rs | |-- context.rs | |-- gdt.rs | |-- idt.rs | |-- instructions.rs | |-- mod.rs | |-- page_table.rs | |-- percpu.rs | |-- syscall.rs | |-- syscall.S | |-- trap.rs | `-- trap.S |-- drivers | |-- interrupt(中断) | | |-- apic.rs | | |-- gicv2.rs | | |-- i8259_pic.rs | | |-- mod.rs | | `-- riscv_intc.rs(riscv中断处理相关) | |-- misc(杂项) | | |-- mod.rs | | |-- psci.rs | | |-- qemu_x86_reset.rs | | `-- sbi.rs(sbi支持) | |-- mod.rs | |-- timer(时钟) | | |-- arm_generic_timer.rs | | |-- mod.rs | | |-- riscv.rs(riscv时钟设置) | | |-- x86_common.rs | | |-- x86_hpet.rs | | `-- x86_tsc.rs | `-- uart(串口) | |-- mod.rs | |-- pl011.rs | |-- riscv.rs(输入输出) | `-- uart16550.rs |-- mm(地址空间) | |-- address.rs(物理/虚拟) | |-- frame_allocator.rs(物理页帧分配器) | |-- heap_allocator.rs(内核动态内存分配) | |-- memory_set.rs(引入地址空间和逻辑段等) | |-- mod.rs(mm初始化方法) | |-- paging.rs(页表抽象和其他内容比如建立和拆除映射关系unmap和map) | `-- uaccess.rs |-- platform(平台相关) | |-- config.rs | |-- mod.rs | |-- pc | | |-- mod.rs | | |-- multiboot.rs | | `-- multiboot.S | |-- qemu_virt_arm | | `-- mod.rs | `-- qemu_virt_riscv | `-- mod.rs |-- sync(同步互斥模块) | |-- lazy_init.rs | |-- mod.rs | |-- mutex.rs(待实现) | |-- percpu.rs | `-- spin.rs |-- syscall(系统调用模块) | |-- fs.rs(sys_read&sys_write) | |-- mod.rs(系统调用分发处理) | |-- task.rs(sys_getpid/fork/exec/waitpid/exit/clone) | `-- time.rs(current_time) |-- task | |-- manager.rs(任务管理器) | |-- mod.rs | |-- schedule(rr调度待实现cfs调度) | | |-- mod.rs | | `-- round_robin.rs | |-- structs.rs(任务相关状态) | `-- wait_queue.rs(等待队列) `-- utils |-- allocator.rs |-- irq_handler.rs |-- mod.rs |-- ratio.rs `-- timer_list.rs |-- config.rs(配置相关包括内存大小、cpu数、调度相关) |-- lang_items.rs(panic处理逻辑) |-- loader.rs(应用加载内存并进行管理) |-- logging.rs(日志输出多级日志和颜色输出) |-- main.rs(主函数) |-- timer.rs |-- percpu.rs (cpu逻辑) ``` ## cfs调度策略 CFS(Completely Fair Scheduler,完全公平调度器)用于Linux系统中普通进程的调度。它给cfs_rq(cfs的run queue)中的每一个进程设置一个虚拟时钟,vruntime。如果一个进程得以执行,随着时间的增长(一个个tick的到来),其vruntime将不断增大。没有得到执行的进程vruntime不变。调度器总是选择vruntime跑得最慢的那个进程来执行。这就是所谓的“完全公平”。为了区别不同优先级的进程,优先级高的进程vruntime增长得慢,以至于它可能得到更多的运行机会。 CFS不区分具体的cpu算力消耗型进程,还是io消耗型进程,统一采用红黑树算法来管理所有的调度实体sched_entity,算法效率为O(log(n))。每个进程都由一个struct task_struct来表示,为什么这里又定义了一个sched_entity?这是由于调度需要一些更详细的信息,比如当前运行时间或上次运行时间,因此就搞出了一个sched_entity的概念,为了存储一些调度相关的信息,给调度器用。CFS跟踪调度实体sched_entity的虚拟运行时间vruntime,平等对待运行队列中的调度实体sched_entity,将执行时间少的调度实体sched_entity排列到红黑树的最左边。调度实体sched_entity通过enqueue_entity()和dequeue_entity()来进行红黑树的出队入队。 2,框架 CFS调度器框架 调度周期:为了保证每个任务都在合理的期限内运行, 我们把时间分成一块块调度周期。而在每个调度周期中, 让每个任务分到同样多的 vruntime。这样的话,至多经过一个调度周期,大家又能运行了。而且任务切换不至于过于频繁。对于单 CPU 而言, 默认设置是小于 8 个就绪任务时就按照 6ms,大于 8 个就绪任务时,每个任务给 0.75 ms的调度粒度。如果 CPU 个数大于 1 个,那么调度周期就算长一点,用户感觉的延迟也会不那么高。因此对于多个CPU 而言,调度粒度还会乘上1+[log2(number of cpus)],拿8核cpu为例,调度粒度是0.75*4=3ms,在调度粒度的时间内进程是不能被抢占的。 调度延迟:从进程加入到运行队列,到被放到cpu上执行经历的时间,和调度周期有关,当运行个数小于等于8时,调度延迟等于调度周期。调度延迟的default值6ms,对于8核cpu,调度延迟=4*6=24ms。当运行队列上的进程数小于等于8时,每个进程至少分到3ms=24/8。 在每个sched_latency内,根据各task的权重值,可以计算出运行时间runtime =调度周期 * 进程权重 / 所有进程权重之和。 根据虚拟运行时间vruntime将各个调度实体用红黑树管理,最左边的节点对应着最小的vruntime。 在调度的时候选择最小vruntime对应的进程进行调度。 虚拟时间vruntime的计算如下:vruntime = runtime*nice_0_weight/seight。 3,数据结构 CFS调度器相关的数据结构 运行队列、cfs运行队列,任务task、任务组、调度实体结构体简介: struct rq:每个CPU都有一个对应的运行队列。 struct cfs_rq:CFS运行队列,该结构中包含了struct rb_root_cached红黑树,用于链接调度实体struct sched_entity。rq运行队列中对应了一个CFS运行队列,此外,在task_group结构中也会为每个CPU再维护一个CFS运行队列。 struct task_struct:任务的描述符,包含了进程的所有信息,该结构中的struct sched_entity,用于参与CFS的调度。 struct task_group:组调度,Linux支持将任务分组来对CPU资源进行分配管理,该结构中为系统中的每个CPU都分配了struct sched_entity调度实体和struct cfs_rq运行队列,其中struct sched_entity用于参与CFS的调度。 struct sched_entity:调度实体,这个也是CFS调度管理的对象。 CFS调度类需要实现的所有接口定义在 struct sched_class 里。下面对其中最重要的一些调度类接口做简单的介绍: enqueue_task:将待运行的任务插入到per-cpu rq。典型的场景就是内核里的唤醒函数,将被唤醒的任务插入rq然后设置任务运行态为 TASK_RUNNING。对 CFS 调度器来说,则是将任务插入红黑树,给 nr_running 增加计数。 dequeue_task:将非运行态任务移除出per-cpu rq。典型的场景就是任务调度引起阻塞的内核函数,把任务运行态设置成 TASK_INTERRUPTIBLE 或 TASK_UNINTERRUPTIBLE,然后调用 schedule 函数,最终触发dequeue_task的操作。对 CFS 调度器来说,则是将不在处于运行态的任务从红黑树中移除,给 nr_running 减少计数。 yield_task:处于运行态的任务申请主动让出 CPU。典型的场景就是处于运行态的应用调用sched_yield系统调用,直接让出 CPU。此时系统调用 sched_yield 系统调用先调用 yield_task 申请让出 CPU,然后调用 schedule 去做上下文切换。对 CFS 调度器来说,如果 nr_running 是 1,则直接返回,最终 schedule 函数也不产生上下文切换。否则,任务被标记为skip 状态。调度器在红黑树上选择待运行任务时肯定会跳过该任务。之后,因为 schedule 函数被调用,pick_next_task 最终会被调用。其代码会从红黑树中最左侧选择一个任务,然后把要放弃运行的任务放回红黑树,然后调用上下文切换函数做任务上下文切换。 check_preempt_curr:用于在待运行任务插入rq后,检查是否应该抢占正在CPU上运行的当前任务。Wakeup Preemption 的实现逻辑主要在这里。对 CFS 调度器而言,主要是在是否能满足调度时延和是否能保证足够任务运行时间之间来取舍。CFS 调度器也提供了预定义的 Threshold 允许做 Wakeup Preemption 的调 pick_next_task:选择下一个最适合调度的任务,将其从rq移除。并且如果前一个任务还保持在运行态,即没有从rq移除,则将当前的任务重新放回到rq。内核 schedule 函数利用它来完成调度时任务的选择。对CFS调度器而言,大多数情况下,下一个调度任务是从红黑树的最左侧节点选择并移除。如果前一个任务是其它调度类,则调用该调度类的 put_prev_task 方法将前一个任务做正确的安置处理。但如果前一个任务如果也属于CFS调度类的话,为了效率,跳过调度类标准方法 put_prev_task,但核心逻辑仍旧是 put_prev_task_fair 的主要部分。 put_prev_task:将前一个正在CPU上运行的任务从CPU上拿下的处理。如果任务还在运行态则将任务放回rq,否则,根据调度类要求做简单处理。此函数通常是 pick_next_task 的密切关联操作,是 schedule 实现的关键部分。如果前一个任务属于CFS调度类,则使用CFS调度类的具体实现 put_prev_task_fair。此时,如果任务还是 TASK_RUNNING 状态,则被重新插入到红黑树的最右侧。如果这个任务不是 TASK_RUNNING 状态,则已经从红黑树移除过了,只需要修改CFS 当前任务指针 cfs_rq->curr 即可。 select_task_rq:为给定的任务选择一个rq,返回rq所属的CPU号。典型的使用场景是唤醒,fork/exec 进程时,给进程选择一个rq,这也给调度器一个CPU负载均衡的机会。对CFS调度器而言,主要是根据传入的参数要求找到符合亲和性要求的最空闲的CPU所属的rq。 set_curr_task:当任务改变自己的调度类或者任务组时,该函数被调用。用户进程可以使用 sched_setscheduler系统调用,通过设置自己新的调度策略来修改自己的调度类。对CFS调度器而言,当任务把自己调度类从其它类型修改成CFS调度类,此时需要把该任务设置成正当前CPU正在运行的任务。例如把任务从红黑树上移除,设置 CFS 当前任务指针 cfs_rq->curr 和调度统计数据等。 task_tick:这个函数通常在系统周期性 (per-tick) 的时钟中断上下文调用,调度类可以把per-tick处理的事务交给该方法执行。例如,调度器的统计数据更新,Tick Preemption的实现逻辑主要在这里。Tick Preemption主要判断是否当前运行任务需要Preemption来被强制剥夺运行。对CFS调度器而言,Tick Preemption 主要是在是否能满足调度时延和是否能保证足够任务运行时间之间来取舍。CFS调度器也提供了预定义的Threshold允许做Tick Preemption的调优。 4,流程分析 整个CFS的核心就是基于cfs调度类实现的各个函数,接下来的流程分析也基于这些核心函数。 4.1 vruntime的计算 CFS调度器没有时间的概念,而是根据虚拟运行时间对任务进行排序,选择虚拟运行时间最小的进程进行调度。虚拟运行时间的运算流程如下: vruntime的计算 计算vruntime是调用sched_vslice实现的,计算的时候依赖调度周期。具体公式:vruntime =(runtime * weight * lw->inv_weight) >> WMULT_SHIFT。其中weight为要调度进程的权重,lw->inv_weight为cfs队列加上要调度进程的总权重,其中runtime的计算是通过sched_slice实现的,sched_slice根据当前进程的权重来计算在CFS就绪队列总权重中可以分到多少调度时间。 Sched_slice中的slice被复用了,首先被表示为调度周期,接下来的for循环中,slice的值 = 调度周期 * 进程权重/ 运行队列的权重。 4.2 任务创建​ 在父进程通过fork创建子进程的时候,task_fork_fair函数会被调用。这个函数的传入参数是子进程的task_struct。该函数的主要作用,就是确定子任务的vruntime,因此也能确定子任务的调度实体在红黑树中的位置。 task_fork_fair本身比较简单,流程如下图: 任务创建 4.3 任务出队入队 当任务进入可运行状态时,需要将调度实体放入到红黑树中,完成入队操作。 当任务退出可运行状态时,需要将调度实体从红黑树中移除,完成出队操作。 CFS调度器使用enqueue_task_fair函数将任务入队到CFS队列,使用dequeue_task_fair函数将任务从CFS队列中出队操作。 任务出队入队 dequeue_task_fair与enqueue_task_fair大体的逻辑类似,这里不再深入分析。 出队与入队的操作中,核心的逻辑可以分成两部分: 1)更新运行时的数据,比如负载、权重、组调度的占比等等。 2)将sched_entity插入红黑树,或者从红黑树移除。 Enqueue task过程中,涉及到了CPU负载计算、task_group组调度、CFS Bandwidth带宽控制等。 4.4 任务选择 每当进程任务切换的时候,也就是schedule函数执行时,调度器需要选择下一个将要执行的任务。在CFS调度器中,是通过pick_next_task_fair函数完成的,流程如下: 任务选择 当需要进程任务切换的时候,pick_next_task_fair函数的传入参数中包含了需要被切换出去的任务,也就是pre_task。 当pre_task不是普通进程时,也就是调度类不是CFS,那么它就不使用sched_entity的调度实体来参与调度,因此会执行simple分支,通过put_pre_task函数来通知系统当前的任务需要被切换,而不是通过put_prev_entity函数来完成。 当pre_task是普通进程时,调用pick_next_entity来选择下一个执行的任务,这个选择过程实际是有两种情况:当调度实体对应task时,do while()遍历一次,当调度实体对应task_group时,则需要遍历任务组来选择下一个执行的任务了。 put_prev_entity,用于切换任务前的准备工作,更新运行时的统计数据,并不进行dequeue的操作,其中需要将CFS队列的curr指针置位成NULL。 set_next_entity,用于设置下一个要运行的调度实体,设置CFS队列的curr指针。 如果使能了hrtimer,则将hrtimer的到期时间设置为调度实体的剩余运行时间。 4.5 cfs调度tick CFS调度器中的tick函数为task_tick_fair,系统的每个调度tick都会调用到,此外如果使用了hrtimer,也会调用到这个函数。流程如下: CFS调度tick 主要的工作包括: 更新运行时的各类统计信息,比如vruntime, 运行时间、负载值、权重值等。 检查是否需要抢占,主要是比较运行时间是否耗尽,以及vruntime的差值是否大于运行时间等。 上面的多个流程中都调用到update_curr函数,我们看一下这个函数具体更新了哪些信息。

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully