Linux CFS調度器之task_tick_fair處理周期性調度器--Linux進程的管理與調度(二十九)

来源:https://www.cnblogs.com/linhaostudy/archive/2018/11/18/9977363.html
-Advertisement-
Play Games

1. CFS如何處理周期性調度器 周期性調度器的工作由scheduler_tick函數完成(定義在 "kernel/sched/core.c, line 2910" ), 在scheduler_tick中周期性調度器通過調用curr進程所屬調度器類sched_class的task_tick函數完成周 ...


1. CFS如何處理周期性調度器

周期性調度器的工作由scheduler_tick函數完成(定義在kernel/sched/core.c, line 2910), 在scheduler_tick中周期性調度器通過調用curr進程所屬調度器類sched_class的task_tick函數完成周期性調度的工作

周期調度的工作形式上sched_class調度器類的task_tick函數完成, CFS則對應task_tick_fair函數, 但實際上工作交給entity_tick完成.

2 CFS的周期性調度

2.1 task_tick_fair與周期性調度

CFS完全公平調度器類通過task_tick_fair函數完成周期性調度的工作, 該函數定義在kernel/sched/fair.c?v=4.6#L8119

/*
 * scheduler tick hitting a task of our scheduling class:
 */
static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued)
{
    struct cfs_rq *cfs_rq;
    /*  獲取到當前進程curr所在的調度實體  */
    struct sched_entity *se = &curr->se;

    /* for_each_sched_entity
     * 在不支持組調度條件下, 只迴圈一次
     * 在組調度的條件下, 調度實體存在層次關係,
     * 更新子調度實體的同時必須更新父調度實體  */
    for_each_sched_entity(se)
    {
        /*  獲取噹噹前運行的進程所在的CFS就緒隊列  */
        cfs_rq = cfs_rq_of(se);
        /*  完成周期性調度  */
        entity_tick(cfs_rq, se, queued);
    }

    if (static_branch_unlikely(&sched_numa_balancing))
        task_tick_numa(rq, curr);
}

我們可以看到, CFS周期性調度的功能實際上是委托給entity_tick函數來完成的

2.2 entity_tick函數

在task_tick_fair中, 內核將CFS周期性調度的實際工作交給了entity_tick來完成, 該函數定義在kernel/sched/fair.c, line 3470中, 如下所示

static void
entity_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr, int queued)
{
    /*
     * Update run-time statistics of the 'current'.
     */
    update_curr(cfs_rq);

    /*
     * Ensure that runnable average is periodically updated.
     */
    update_load_avg(curr, 1);
    update_cfs_shares(cfs_rq);

#ifdef CONFIG_SCHED_HRTICK
    /*
     * queued ticks are scheduled to match the slice, so don't bother
     * validating it and just reschedule.
     */
    if (queued) {
        resched_curr(rq_of(cfs_rq));
        return;
    }
    /*
     * don't let the period tick interfere with the hrtick preemption
     */
    if (!sched_feat(DOUBLE_TICK) &&
            hrtimer_active(&rq_of(cfs_rq)->hrtick_timer))
        return;
#endif

    if (cfs_rq->nr_running > 1)
        check_preempt_tick(cfs_rq, curr);
}

首先, 一如既往的使用update_curr來更新統計量

接下來是hrtimer的更新, 這些由內核通過參數CONFIG_SCHED_HRTICK開啟

然後如果cfs就緒隊列中進程數目nr_running少於兩個(< 2)則實際上無事可做. 因為如果某個進程應該被搶占, 那麼至少需要有另一個進程能夠搶占它(即cfs_rq->nr_running > 1)

如果進程的數目不少於兩個, 則由check_preempt_tick作出決策

if (cfs_rq->nr_running > 1)
        check_preempt_tick(cfs_rq, curr);

2.3 check_preempt_tick函數

在entity_tick中, 如果cfs的就緒隊列中進程數目不少於2, 說明至少需要有另外一個進程能夠搶占當前進程, 此時內核交給check_preempt_tick作出決策. check_preempt_tick函數定義在kernel/sched/fair.c, line 3308

/*
 * Preempt the current task with a newly woken task if needed:
 */
static void
check_preempt_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr)
{
    unsigned long ideal_runtime, delta_exec;
    struct sched_entity *se;
    s64 delta;

    /*  計算curr的理論上應該運行的時間  */
    ideal_runtime = sched_slice(cfs_rq, curr);

    /*  計算curr的實際運行時間
     *  sum_exec_runtime: 進程執行的總時間
     *  prev_sum_exec_runtime:進程在切換進CPU時的sum_exec_runtime值  */
    delta_exec = curr->sum_exec_runtime - curr->prev_sum_exec_runtime;

    /*  如果實際運行時間比理論上應該運行的時間長
     *  說明curr進程已經運行了足夠長的時間
     *  應該調度新的進程搶占CPU了  */
    if (delta_exec > ideal_runtime)
    {
        resched_curr(rq_of(cfs_rq));
        /*
         * The current task ran long enough, ensure it doesn't get
         * re-elected due to buddy favours.
         */
        clear_buddies(cfs_rq, curr);
        return;
    }

    /*
     * Ensure that a task that missed wakeup preemption by a
     * narrow margin doesn't have to wait for a full slice.
     * This also mitigates buddy induced latencies under load.
     */
    if (delta_exec < sysctl_sched_min_granularity)
        return;

    se = __pick_first_entity(cfs_rq);
    delta = curr->vruntime - se->vruntime;

    if (delta < 0)
        return;

    if (delta > ideal_runtime)
        resched_curr(rq_of(cfs_rq));
}

check_preempt_tick函數的目的在於, 判斷是否需要搶占當前進程. 確保沒有哪個進程能夠比延遲周期中確定的份額運行得更長. 該份額對應的實際時間長度在sched_slice中計算.

而上一節我們提到, 進程在CPU上已經運行的實際時間間隔由sum_exec_runtime - prev_sum_runtime給出.

還記得上一節, 在set_next_entity函數的最後, 將選擇出的調度實體se的sum_exec_runtime保存在了prev_sum_exec_runtime中, 因為該調度實體指向的進程, 馬上將搶占處理器成為當前活動進程, 在CPU上花費的實際時間將記入sum_exec_runtime, 因此內核會在prev_sum_exec_runtime保存此前的設置. 要註意進程中的sum_exec_runtime沒有重置. 因此差值sum_exec_runtime - prev_sum_runtime確實標識了在CPU上執行花費的實際時間.

在處理周期性調度時, 這個差值就顯得格外重要

因此搶占決策很容易做出決定, 如果檢查發現當前進程運行需要被搶占, 那麼通過resched_task發出重調度請求. 這會在task_struct中設置TIF_NEED_RESCHED標誌, 核心調度器會在下一個適當的時機發起重調度.

其實需要搶占的條件有下麵兩種可能性

  • curr進程的實際運行時間delta_exec比期望的時間間隔ideal_runtime長

此時說明curr進程已經運行了足夠長的時間

  • curr進程與紅黑樹中最左進程left虛擬運行時間的差值大於curr的期望運行時間ideal_runtime

此時說明紅黑樹中最左結點left與curr節點更渴望處理器, 已經接近於饑餓狀態, 這個我們可以這樣理解, 相對於curr進程來說, left進程如果參與調度, 其期望運行時間應該域curr進程的期望時間ideal_runtime相差不大, 而此時如果curr->vruntime - se->vruntime > curr.ideal_runtime, 我們可以初略的理解為curr進程已經優先於left進程多運行了一個周期, 而left又是紅黑樹總最饑渴的那個進程, 因此curr進程已經遠遠領先於隊列中的其他進程, 此時應該補償其他進程。

如果檢查需要發生搶占, 則內核通過resched_curr(rq_of(cfs_rq))設置重調度標識, 從而觸發延遲調度

2.4 resched_curr設置重調度標識TIF_NEED_RESCHED

周期性調度器並不顯式進行調度, 而是採用了延遲調度的策略, 如果發現需要搶占, 周期性調度器就設置進程的重調度標識TIF_NEED_RESCHED, 然後由主調度器完成調度工作.

TIF_NEED_RESCHED標識, 表明進程需要被調度, TIF首碼表明這是一個存儲在進程thread_info中flag欄位的一個標識信息

在內核的一些關鍵位置, 會檢查當前進程是否設置了重調度標誌TLF_NEDD_RESCHED, 如果該進程被其他進程設置了TIF_NEED_RESCHED標誌, 則函數重新執行進行調度

前面我們在check_preempt_tick中如果發現curr進程已經運行了足夠長的時間, 其他進程已經開始饑餓, 那麼我們就需要通過resched_curr來設置重調度標識TIF_NEED_RESCHED

resched_curr函數定義在kernel/sched/core.c, line 446中, 並沒有什麼複雜的工作, 其實就是通過set_tsk_need_resched(curr);函數設置重調度標識

3 總結

周期性調度器的工作由scheduler_tick函數完成(定義在kernel/sched/core.c, line 2910), 在scheduler_tick中周期性調度器通過調用curr進程所屬調度器類sched_class的task_tick函數完成周期性調度的工作

周期調度的工作形式上sched_class調度器類的task_tick函數完成, CFS則對應task_tick_fair函數, 但實際上工作交給entity_tick完成.

而entity_tick中則通過check_preempt_tick函數檢查是否需要搶占當前進程curr, 如果發現curr進程已經運行了足夠長的時間, 其他進程已經開始饑餓, 那麼我們就需要通過resched_curr函數來設置重調度標識TIF_NEED_RESCHED

其中check_preempt_tick檢查可搶占的條件如下

  • curr進程的實際運行時間delta_exec比期望的時間間隔ideal_runtime長, 此時說明curr進程已經運行了足夠長的時間

  • curr進程與紅黑樹中最左進程left虛擬運行時間的差值大於curr的期望運行時間ideal_runtime, 此時我們可以理解為curr進程已經優先於left進程多運行了一個周期, 而left又是紅黑樹總最饑渴的那個進程, 因此curr進程已經遠遠領先於隊列中的其他進程, 此時應該補償其他進程


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

-Advertisement-
Play Games
更多相關文章
  • 在asp.net mvc 中,action方法里根據參數獲取數據,假如獲取的數據為空,為了響應404錯誤頁,我們可以return HttpNotFound(); 但是在asp.net webform中,實現方式就不一樣了。 為了體現本人在實現過程中的所遇到的問題,現舉例來說明。 1. 在asp.ne ...
  • 一、簡介 eShopOnContainers是一個簡化版的基於.NET Core和Docker等技術開發的面向微服務架構的參考應用,是一個簡化版的線上商城/電子商務應用,其包含基於瀏覽器的Web應用、基於Xamarin的Android、IOS、Windows/UWP 移動應用,以及對應的服務端支撐。 ...
  • CFSSL是CloudFlare開源的一款PKI/TLS工具。 CFSSL 包含一個命令行工具 和一個用於 簽名,驗證並且捆綁TLS證書的 HTTP API 服務。 使用Go語言編寫。 Github 地址: "https://github.com/cloudflare/cfssl" 官網地址: "h ...
  • 1. 3種系統架構與2種存儲器共用方式 1.1 架構概述 從系統架構來看,目前的商用伺服器大體可以分為三類 對稱多處理器結構(SMP:Symmetric Multi Processor) 非一致存儲訪問結構(NUMA:Non Uniform Memory Access) 海量並行處理結構(MPP:M ...
  • 源碼安裝 linux上的軟體大部分都是c語言開發的 , 那麼安裝需要gcc編譯程式才可以進行源碼安裝. yum install -y gcc #先安裝gcc 安裝源碼需要三個步驟 1) ./configure 在這一步可以定製功能 , 加上相應的選項即可 , 具有什麼選項可以通過 ./configu ...
  •     case語句相當於多分支的if/elif/else語句,而在使用case會讓腳本看起來更簡單工整。在case語句中,程式會將獲取到的值與case表達式中的條件逐一進行對比,如果匹配則執行對應的語句,遇到雙分號(;;)則停止執行。如果一直未找到匹配項,則執行 \ ) 後面的 ...
  • rpm 包安裝 RedHat Package Manager的縮寫 , linux 的軟體包可能存在依賴關係,比如某某依賴某某才能使用。 掛載一個光碟 mount -t auto /dev/cdrom /mnt/rom #掛載光碟 安裝一個rpm包 rpm -vim [文件名] "-i":安裝的意思 ...
  • 按照POSIX標準的強制要求,除了“普通”進程之外, Linux還支持兩種實時調度類。調度器結構使得實時進程可以平滑地集成到內核中,而無需修改核心調度器,這顯然是調度類帶來的好處。 現在比較適合於回想一些很久以前討論過的事實。實時進程的特點在於其優先順序比普通進程高,對應地,其static_prio值 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...