【原創】(一)Linux進程調度器-基礎

来源:https://www.cnblogs.com/LoyenWang/archive/2020/02/01/12249106.html
-Advertisement-
Play Games

背景 By 魯迅 By 高爾基 說明: 1. Kernel版本:4.14 2. ARM64處理器,Contex A53,雙核 3. 使用工具:Source Insight 3.5, Visio 1. 概述 從這篇文章開始,將開始Linux調度器的系列研究了。 本文也會從一些基礎的概念及數據結構入手, ...


背景

  • Read the fucking source code! --By 魯迅
  • A picture is worth a thousand words. --By 高爾基

說明:

  1. Kernel版本:4.14
  2. ARM64處理器,Contex-A53,雙核
  3. 使用工具:Source Insight 3.5, Visio

1. 概述

從這篇文章開始,將開始Linux調度器的系列研究了。
本文也會從一些基礎的概念及數據結構入手,先打造一個粗略的輪廓,後續的文章將逐漸深入。

2. 概念

2.1 進程

  • 從教科書上,我們都能知道:進程是資源分配的最小單位,而線程是CPU調度的的最小單位。
  • 進程不僅包括可執行程式的代碼段,還包括一系列的資源,比如:打開的文件、記憶體、CPU時間、信號量、多個執行線程流等等。而線程可以共用進程內的資源空間。
  • 在Linux內核中,進程和線程都使用struct task_struct結構來進行抽象描述。
  • 進程的虛擬地址空間分為用戶虛擬地址空間和內核虛擬地址空間,所有進程共用內核虛擬地址空間,沒有用戶虛擬地址空間的進程稱為內核線程。

Linux內核使用task_struct結構來抽象,該結構包含了進程的各類信息及所擁有的資源,比如進程的狀態、打開的文件、地址空間信息、信號資源等等。task_struct結構很複雜,下邊只針對與調度相關的某些欄位進行介紹。

struct task_struct {
    /* ... */
    
    /* 進程狀態 */
    volatile long           state;

    /* 調度優先順序相關,策略相關 */
    int             prio;
    int             static_prio;
    int             normal_prio;
    unsigned int            rt_priority;
    unsigned int            policy;
    
    /* 調度類,調度實體相關,任務組相關等 */
    const struct sched_class    *sched_class;
    struct sched_entity     se;
    struct sched_rt_entity      rt;
#ifdef CONFIG_CGROUP_SCHED
    struct task_group       *sched_task_group;
#endif
    struct sched_dl_entity      dl;
    
    /* 進程之間的關係相關 */
        /* Real parent process: */
    struct task_struct __rcu    *real_parent;

    /* Recipient of SIGCHLD, wait4() reports: */
    struct task_struct __rcu    *parent;

    /*
     * Children/sibling form the list of natural children:
     */
    struct list_head        children;
    struct list_head        sibling;
    struct task_struct      *group_leader;
    
    /* ... */
}

2.2 進程狀態

  • 上圖中左側為操作系統中通俗的進程三狀態模型,右側為Linux對應的進程狀態切換。每一個標誌描述了進程的當前狀態,這些狀態都是互斥的;
  • Linux中的就緒態運行態對應的都是TASK_RUNNING標誌位,就緒態表示進程正處在隊列中,尚未被調度;運行態則表示進程正在CPU上運行;

內核中主要的狀態欄位定義如下

/* Used in tsk->state: */
#define TASK_RUNNING            0x0000
#define TASK_INTERRUPTIBLE      0x0001
#define TASK_UNINTERRUPTIBLE        0x0002

/* Used in tsk->exit_state: */
#define EXIT_DEAD           0x0010
#define EXIT_ZOMBIE         0x0020
#define EXIT_TRACE          (EXIT_ZOMBIE | EXIT_DEAD)

/* Used in tsk->state again: */
#define TASK_PARKED         0x0040
#define TASK_DEAD           0x0080
#define TASK_WAKEKILL           0x0100
#define TASK_WAKING         0x0200
#define TASK_NOLOAD         0x0400
#define TASK_NEW            0x0800
#define TASK_STATE_MAX          0x1000

/* Convenience macros for the sake of set_current_state: */
#define TASK_KILLABLE           (TASK_WAKEKILL | TASK_UNINTERRUPTIBLE)
#define TASK_STOPPED            (TASK_WAKEKILL | __TASK_STOPPED)
#define TASK_TRACED         (TASK_WAKEKILL | __TASK_TRACED)

#define TASK_IDLE           (TASK_UNINTERRUPTIBLE | TASK_NOLOAD)

2.3 scheduler 調度器

  • 所謂調度,就是按照某種調度的演算法,從進程的就緒隊列中選取進程分配CPU,主要是協調對CPU等的資源使用。進程調度的目標是最大限度利用CPU時間。

內核預設提供了5個調度器,Linux內核使用struct sched_class來對調度器進行抽象:

  1. Stop調度器, stop_sched_class:優先順序最高的調度類,可以搶占其他所有進程,不能被其他進程搶占;
  2. Deadline調度器, dl_sched_class:使用紅黑樹,把進程按照絕對截止期限進行排序,選擇最小進程進行調度運行;
  3. RT調度器, rt_sched_class:實時調度器,為每個優先順序維護一個隊列;
  4. CFS調度器, cfs_sched_class:完全公平調度器,採用完全公平調度演算法,引入虛擬運行時間概念;
  5. IDLE-Task調度器, idle_sched_class:空閑調度器,每個CPU都會有一個idle線程,當沒有其他進程可以調度時,調度運行idle線程;

Linux內核提供了一些調度策略供用戶程式來選擇調度器,其中Stop調度器IDLE-Task調度器,僅由內核使用,用戶無法進行選擇:

  • SCHED_DEADLINE:限期進程調度策略,使task選擇Deadline調度器來調度運行;
  • SCHED_RR:實時進程調度策略,時間片輪轉,進程用完時間片後加入優先順序對應運行隊列的尾部,把CPU讓給同優先順序的其他進程;
  • SCHED_FIFO:實時進程調度策略,先進先出調度沒有時間片,沒有更高優先順序的情況下,只能等待主動讓出CPU;
  • SCHED_NORMAL:普通進程調度策略,使task選擇CFS調度器來調度運行;
  • SCHED_BATCH:普通進程調度策略,批量處理,使task選擇CFS調度器來調度運行;
  • SCHED_IDLE:普通進程調度策略,使task以最低優先順序選擇CFS調度器來調度運行;

2.4 runqueue 運行隊列

  • 每個CPU都有一個運行隊列,每個調度器都作用於運行隊列;
  • 分配給CPU的task,作為調度實體加入到運行隊列中;
  • task首次運行時,如果可能,儘量將它加入到父task所在的運行隊列中(分配給相同的CPU,緩存affinity會更高,性能會有改善);

Linux內核使用struct rq結構來描述運行隊列,關鍵欄位如下:

/*
 * This is the main, per-CPU runqueue data structure.
 *
 * Locking rule: those places that want to lock multiple runqueues
 * (such as the load balancing or the thread migration code), lock
 * acquire operations must be ordered by ascending &runqueue.
 */
struct rq {
    /* runqueue lock: */
    raw_spinlock_t lock;

    /*
     * nr_running and cpu_load should be in the same cacheline because
     * remote CPUs use both these fields when doing load calculation.
     */
    unsigned int nr_running;
    
    /* 三個調度隊列:CFS調度,RT調度,DL調度 */
    struct cfs_rq cfs;
    struct rt_rq rt;
    struct dl_rq dl;

    /* stop指向遷移內核線程, idle指向空閑內核線程 */
    struct task_struct *curr, *idle, *stop;
    
    /* ... */
}    

2.5 task_group 任務分組

  • 利用任務分組的機制,可以設置或限制任務組對CPU的利用率,比如將某些任務限制在某個區間內,從而不去影響其他任務的執行效率;
  • 引入task_group後,調度器的調度對象不僅僅是進程了,Linux內核抽象出了sched_entity/sched_rt_entity/sched_dl_entity描述調度實體,調度實體可以是進程或task_group
  • 使用數據結構struct task_group來描述任務組,任務組在每個CPU上都會維護一個CFS調度實體、CFS運行隊列,RT調度實體,RT運行隊列

Linux內核使用struct task_group來描述任務組,關鍵的欄位如下:

/* task group related information */
struct task_group {
    /* ... */

    /* 為每個CPU都分配一個CFS調度實體和CFS運行隊列 */
#ifdef CONFIG_FAIR_GROUP_SCHED
    /* schedulable entities of this group on each cpu */
    struct sched_entity **se;
    /* runqueue "owned" by this group on each cpu */
    struct cfs_rq **cfs_rq;
    unsigned long shares;
#endif

    /* 為每個CPU都分配一個RT調度實體和RT運行隊列 */
#ifdef CONFIG_RT_GROUP_SCHED
    struct sched_rt_entity **rt_se;
    struct rt_rq **rt_rq;

    struct rt_bandwidth rt_bandwidth;
#endif

    /* task_group之間的組織關係 */
    struct rcu_head rcu;
    struct list_head list;

    struct task_group *parent;
    struct list_head siblings;
    struct list_head children;

    /* ... */
};

3. 調度程式

調度程式依靠幾個函數來完成調度工作的,下邊將介紹幾個關鍵的函數。

  1. 主動調度 - schedule()
  • schedule()函數,是進程調度的核心函數,大體的流程如上圖所示。
  • 核心的邏輯:選擇另外一個進程來替換掉當前運行的進程。進程的選擇是通過進程所使用的調度器中的pick_next_task函數來實現的,不同的調度器實現的方法不一樣;進程的替換是通過context_switch()來完成切換的,具體的細節後續的文章再深入分析。
  1. 周期調度 - schedule_tick()
  • 時鐘中斷處理程式中,調用schedule_tick()函數;
  • 時鐘中斷是調度器的脈搏,內核依靠周期性的時鐘來處理器CPU的控制權;
  • 時鐘中斷處理程式,檢查當前進程的執行時間是否超額,如果超額則設置重新調度標誌(_TIF_NEED_RESCHED);
  • 時鐘中斷處理函數返回時,被中斷的進程如果在用戶模式下運行,需要檢查是否有重新調度標誌,設置了則調用schedule()調度;
  1. 高精度時鐘調度 - hrtick()
  • 高精度時鐘調度,與周期性調度類似,不同點在於周期調度的精度為ms級別,而高精度調度的精度為ns級別;
  • 高精度時鐘調度,需要有對應的硬體支持;
  1. 進程喚醒時調度 - wake_up_process()
  • 喚醒進程時調用wake_up_process()函數,被喚醒的進程可能搶占當前的進程;

上述講到的幾個函數都是常用於調度時調用。此外,在創建新進程時,或是在內核搶占時,也會出現一些調度點。

本文只是粗略的介紹了一個大概,後續將針對某些模塊進行更加深入的分析,敬請期待。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 迴圈結構分兩大類,一類是當型,一類是直到型。 當型: 當布爾表達式條件為true時,反覆執行某語句,當布爾表達式的值為false時才停止迴圈,比如:while與for迴圈。 直到型: 先執行某語句, 再判斷布爾表達式,如果為true,再執行某語句,如此反覆,直到布爾表達式條件為false時才停止迴圈 ...
  • 1. Tomcat介紹 Tomcat簡單的說就是一個運行Java Web項目的網路伺服器,底層是Socket的一個程式,它也是JSP和Servlet的一個容器。 2. Tomcat的安裝 Tomcat是使用Java語言編寫的一個伺服器,它的安裝需要依賴系統有Java JDK,且安裝版本需要和電腦環境 ...
  • 微信公眾號: "Dotnet9" ,網站: "Dotnet9" ,問題或建議: "請網站留言" , 如果對您有所幫助: "歡迎贊賞" 。 簡易音樂播放器主界面設計 .NET CORE(C ) WPF開發 閱讀導航 1. 本文背景 2. 代碼實現 3. 本文參考 4. 源碼 1. 本文背景 繼續 Ma ...
  • 內容控制項(content control)是更特殊的控制項類型,它們可包含並顯示一塊內容。從技術角度看,內容控制項時可以包含單個嵌套元素的控制項。與佈局容器不同的是,內容控制項只能包含一個子元素,而佈局容器主要願意可以包含任意多個牽頭元素。 正如前面所介紹,所有WPF佈局容器都繼承自抽象類Panel,該類提 ...
  • 使用 F# 手寫一個 Typedoc 轉 C# 代碼生成器,方便一切 C# 項目對 TypeScript 項目的封裝。 ...
  • Equals和GetHashCode Equals每個實現都必須遵循以下約定: 自反性(Reflexive): x.equals(x)必須返回true. 對稱性(Symmetric): x.equals(y)為true時,y.equals(x)也為true. 傳遞性(Transitive): 對於任 ...
  • (1)切換到目錄 /usr/bin; (2)查看目錄/usr/local 下所有的文件; (3)進入/usr 目錄,創建一個名為 test 的目錄,並查看有多少目錄存在; (4)在/usr 下新建目錄 test1,再複製這個目錄內容到/tmp; (5)將上面的/tmp/test1 目錄重命名為 te ...
  • 在 Docker 官網查閱 API 調用方式 例如: "查詢正在運行的容器列表" ,HTTP 方式如下: 分析 API 請求的過程 在本機執行如下命令 Java 模擬調用 API 的代碼實現 1、引入 UnixSocket 工具包 2、測試代碼 相關文檔 "Docker API 文檔" 本文由博客一 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...