Linux核心調度器之周期性調度器scheduler_tick--Linux進程的管理與調度(十八)

来源:https://www.cnblogs.com/linhaostudy/archive/2018/10/28/9867364.html
-Advertisement-
Play Games

我們前面提到linux有兩種方法激活調度器:核心調度器和 周期調度器 一種是直接的, 比如進程打算睡眠或出於其他原因放棄CPU 另一種是通過周期性的機制, 以固定的頻率運行, 不時的檢測是否有必要 因而內核提供了兩個調度器 主調度器 , 周期性調度器 ,分別實現如上工作, 兩者合在一起就組成了 核心 ...


我們前面提到linux有兩種方法激活調度器:核心調度器和
周期調度器

  • 一種是直接的, 比如進程打算睡眠或出於其他原因放棄CPU
  • 另一種是通過周期性的機制, 以固定的頻率運行, 不時的檢測是否有必要

因而內核提供了兩個調度器主調度器周期性調度器,分別實現如上工作, 兩者合在一起就組成了核心調度器(core scheduler), 也叫通用調度器(generic scheduler).

他們都根據進程的優先順序分配CPU時間, 因此這個過程就叫做優先調度, 我們將在本節主要講解核心調度器的設計和優先調度的實現方式.

而我們的周期性調度器以固定的頻率激活負責當前進程調度類的周期性調度方法, 以保證系統的併發性

1 前景回顧

首先還是讓我們簡單回顧一下子之前的的內容

1.1 進程調度

記憶體中保存了對每個進程的唯一描述, 並通過若幹結構與其他進程連接起來.

調度器面對的情形就是這樣, 其任務是在程式之間共用CPU時間, 創造並行執行的錯覺, 該任務分為兩個不同的部分, 其中一個涉及調度策略, 另外一個涉及上下文切換.

內核必須提供一種方法, 在各個進程之間儘可能公平地共用CPU時間, 而同時又要考慮不同的任務優先順序.

調度器的一般原理是, 按所需分配的計算能力, 向系統中每個進程提供最大的公正性, 或者從另外一個角度上說, 他試圖確保沒有進程被虧待.

1.2 進程的分類

linux把進程區分為實時進程和非實時進程, 其中非實時進程進一步劃分為互動式進程和批處理進程

根據進程的不同分類Linux採用不同的調度策略.

對於實時進程,採用FIFO, Round Robin或者Earliest Deadline First (EDF)最早截止期限優先調度演算法|的調度策略.

對於普通進程則採用CFS完全公平調度器進行調度

1.3 linux調度器的演變

欄位 版本
O(n)的始調度演算法 linux-0.11~2.4
O(1)調度器 linux-2.5
CFS調度器 linux-2.6~至今

1.4 Linux的調度器組成

2個調度器

可以用兩種方法來激活調度

  • 一種是直接的, 比如進程打算睡眠或出於其他原因放棄CPU
  • 另一種是通過周期性的機制, 以固定的頻率運行, 不時的檢測是否有必要

因此當前linux的調度程式由兩個調度器組成:主調度器,周期性調度器(兩者又統稱為通用調度器(generic scheduler)或核心調度器(core scheduler))

並且每個調度器包括兩個內容:調度框架(其實質就是兩個函數框架)及調度器類

6種調度策略

linux內核目前實現了6中調度策略(即調度演算法), 用於對不同類型的進程進行調度, 或者支持某些特殊的功能

  • SCHED_NORMAL和SCHED_BATCH調度普通的非實時進程
  • SCHED_FIFO和SCHED_RR和SCHED_DEADLINE則採用不同的調度策略調度實時進程
  • SCHED_IDLE則在系統空閑時調用idle進程.

5個調度器類

而依據其調度策略的不同實現了5個調度器類, 一個調度器類可以用一種種或者多種調度策略調度某一類進程, 也可以用於特殊情況或者調度特殊功能的進程.

其所屬進程的優先順序順序為

stop_sched_class -> dl_sched_class -> rt_sched_class -> fair_sched_class -> idle_sched_class

3個調度實體

調度器不限於調度進程, 還可以調度更大的實體, 比如實現組調度.

這種一般性要求調度器不直接操作進程, 而是處理可調度實體, 因此需要一個通用的數據結構描述這個調度實體,即seched_entity結構, 其實際上就代表了一個調度對象,可以為一個進程,也可以為一個進程組.

linux中針對當前可調度的實時和非實時進程, 定義了類型為seched_entity的3個調度實體

  • sched_dl_entity 採用EDF演算法調度的實時調度實體
    sched_rt_entity
  • 採用Roound-Robin或者FIFO演算法調度的實時調度實體 rt_sched_class
  • sched_entity 採用CFS演算法調度的普通非實時進程的調度實體

2 周期性調度器

周期性調度器在scheduler_tick中實現. 如果系統正在活動中, 內核會按照頻率HZ自動調用該函數. 如果沒有近曾在等待調度, 那麼在電腦電力供應不足的情況下, 內核將關閉該調度器以減少能耗. 這對於我們的嵌入式設備或者手機終端設備的電源管理是很重要的.

2.1 周期性調度器主流程

scheduler_tick函數定義在kernel/sched/core.c, L2910中, 它有兩個主要任務

  1. 更新相關統計量

管理內核中的與整個系統和各個進程的調度相關的統計量. 其間執行的主要操作是對各種計數器+1

  1. 激活負責當前進程調度類的周期性調度方法

檢查進程執行的時間是否超過了它對應的ideal_runtime,如果超過了,則告訴系統,需要啟動主調度器(schedule)進行進程切換。(註意thread_info:preempt_count、thread_info:flags (TIF_NEED_RESCHED))

/*
 * This function gets called by the timer code, with HZ frequency.
 * We call it with interrupts disabled.
 */

void scheduler_tick(void)
{
    /*  1.  獲取當前cpu上的全局就緒隊列rq和當前運行的進程curr  */

    /*  1.1 在於SMP的情況下,獲得當前CPU的ID。如果不是SMP,那麼就返回0  */
    int cpu = smp_processor_id();

    /*  1.2 獲取cpu的全局就緒隊列rq, 每個CPU都有一個就緒隊列rq  */
    struct rq *rq = cpu_rq(cpu);

    /*  1.3 獲取就緒隊列上正在運行的進程curr  */
    struct task_struct *curr = rq->curr;

    sched_clock_tick();

    /*  2 更新rq上的統計信息, 並執行進程對應調度類的周期性的調度  */

    /*  加鎖 */
    raw_spin_lock(&rq->lock);

    /*  2.1 更新rq的當前時間戳.即使rq->clock變為當前時間戳  */
    update_rq_clock(rq);

    /*  2.2 執行當前運行進程所在調度類的task_tick函數進行周期性調度  */
    curr->sched_class->task_tick(rq, curr, 0);

    /*  2.3 更新rq的負載信息,  即就緒隊列的cpu_load[]數據
     *  本質是講數組中先前存儲的負荷值向後移動一個位置,
     *  將當前負荷記入數組的第一個位置 */
    update_cpu_load_active(rq);



    /*  2.4 更新cpu的active count活動計數
     *  主要是更新全局cpu就緒隊列的calc_load_update*/
    calc_global_load_tick(rq);

    /* 解鎖 */
    raw_spin_unlock(&rq->lock);

    /* 與perf計數事件相關 */
    perf_event_task_tick();

#ifdef CONFIG_SMP

     /* 當前CPU是否空閑 */
    rq->idle_balance = idle_cpu(cpu);

    /* 如果到是時候進行周期性負載平衡則觸發SCHED_SOFTIRQ */
    trigger_load_balance(rq);

#endif

    rq_last_tick_reset(rq);
}

2.2 更新統計量

函數 描述 定義
update_rq_clock 處理就緒隊列時鐘的更新, 本質上就是增加struct rq當前實例的時鐘時間戳 sched/core.c, L98
update_cpu_load_active 負責更新就緒隊列的cpu_load數組, 其本質上相當於將數組中先前存儲的負荷值向後移動一個位置, 將當前就緒隊列的符合記入數組的第一個位置. 另外該函數還引入一些取平均值的技巧, 以確保符合數組的內容不會呈現太多的不聯繫跳讀. kernel/sched/fair.c, L4641
calc_global_load_tick 跟新cpu的活動計數, 主要是更新全局cpu就緒隊列的calc_load_update kernel/sched/loadavg.c, L382

2.3 激活進程所屬調度類的周期性調度器

由於調度器的模塊化結構, 主體工程其實很簡單, 在更新統計信息的同時, 內核將真正的調度工作委托給了特定的調度類方法

內核先找到了就緒隊列上當前運行的進程curr, 然後調用curr所屬調度類sched_class的周期性調度方法task_tick

curr->sched_class->task_tick(rq, curr, 0);

task_tick的實現方法取決於底層的調度器類, 例如完全公平調度器會在該方法中檢測是否進程已經運行了太長的時間, 以避免過長的延遲, 註意此處的做法與之前就的基於時間片的調度方法有本質區別, 舊的方法我們稱之為到期的時間片, 而完全公平調度器CFS中則不存在所謂的時間片概念.

目前我們的內核中的3個調度器類struct sched_entity, struct sched_rt_entity, 和struct sched_dl_entity dl, 我們針對當前內核中實現的調度器類分別列出其周期性調度函數task_tick

調度器類 task_tick操作 task_tick函數定義
stop_sched_class - kernel/sched/stop_task.c, line 77, task_tick_stop
dl_sched_class - kernel/sched/deadline.c, line 1192, task_tick_dl
rt_sched_class - /kernel/sched/rt.c, line 2227, task_tick_rt
fail_sched_class - kernel/sched/fair.c, line 8116, task_tick_fail
idle_sched_class - kernel/sched/idle_task.c, line 53, task_tick_idle
idle_sched_class - kernel/sched/idle_task.c, line 53, task_tick_idle

如果當前進程是完全公平隊列中的進程, 則首先根據當前就緒隊列中的進程數算出一個延遲時間間隔,大概每個進程分配2ms時間,然後按照該進程在隊列中的總權重中占得比例,算出它該執行的時間X,如果該進程執行物理時間超過了X,則激發延遲調度;如果沒有超過X,但是紅黑樹就緒隊列中下一個進程優先順序更高,即curr->vruntime-leftmost->vruntime > X,也將延遲調度

延遲調度的真正調度過程在:schedule中實現,會按照調度類順序和優先順序挑選出一個最高優先順序的進程執行

  • 如果當前進程是實時調度類中的進程:則如果該進程是SCHED_RR,則遞減時間片[為HZ/10],到期,插入到隊列尾部,並激發延遲調度,如果是SCHED_FIFO,則什麼也不做,直到該進程執行完成

如果當前進程希望被重新調度, 那麼調度類方法會在task_struct中設置TIF_NEED_RESCHED標誌, 以表示該請求, 而內核將會在接下來的適當實際完成此請求.

3 周期性調度器的激活

3.1 定時器周期性的激活調度器

定時器是Linux提供的一種定時服務的機制. 它在某個特定的時間喚醒某個進程,來做一些工作.

在低解析度定時器的每次時鐘中斷完成全局統計量更新後, 每個cpu在軟中斷中執行一下操作

  • 更新該cpu上當前進程內核態、用戶態使用時間xtime_update
  • 調用該cpu上的定時器函數
  • 啟動周期性定時器(scheduler_tick)完成該cpu上任務的周期性調度工作;

在支持動態定時器的系統中,可以關閉該調度器,從而進入深度睡眠過程;scheduler_tick查看當前進程是否運行太長時間,如果是,將進程的TIF_NEED_RESCHED置位,然後再中斷返回時,調用schedule,進行進程切換操作

//  http://lxr.free-electrons.com/source/arch/arm/kernel/time.c?v=4.6#L74
/*
* Kernel system timer support.
*/
void timer_tick(void)
{
    profile_tick(CPU_PROFILING);
    xtime_update(1);
#ifndef CONFIG_SMP
    update_process_times(user_mode(get_irq_regs()));
#endif
}

//  http://lxr.free-electrons.com/source/kernel/time/timer.c?v=4.6#L1409
/*
 * Called from the timer interrupt handler to charge one tick to the current
 * process.  user_tick is 1 if the tick is user time, 0 for system.
 */
void update_process_times(int user_tick)
{
    struct task_struct *p = current;

    /* Note: this timer irq context must be accounted for as well. */
    account_process_tick(p, user_tick);
    run_local_timers();
    rcu_check_callbacks(user_tick);
#ifdef CONFIG_IRQ_WORK
    if (in_irq())
        irq_work_tick();
#endif
    scheduler_tick();
    run_posix_cpu_timers(p);
}

早期實現

Linux初始化時, init_IRQ()函數設定8253的定時周期為10ms(一個tick值). 同樣,在初始化時, time_init()用setup_irq()設置時間中斷向量irq0, 中斷服務程式為timer_interrupt.

在2.4版內核及較早的版本當中, 定時器的中斷處理採用底半機制, 底半處理函數的註冊在start_kernel()函數中調用sechd_init(), 在這個函數中又調用init_bh(TIMER_BH, timer_bh)註冊了定時器的底半處理函數. 然後系統才調用time_init( )來註冊定時器的中斷向量和中斷處理函數.

在中斷處理函數timer_interrupt()中,主要是調用do_timer()函數完成工作。do_timer()函數的主要功能就是調用mark_bh()產生軟中斷,隨後處理器會在合適的時候調用定時器底半處理函數timer_bh()。在timer_bh()中, 實現了更新定時器的功能. 2.4.23版的do_timer()函數代碼如下(經過簡略):

void do_timer(struct pt_regs *regs)
{
       (*(unsigned long *)&jiffies)++;
       update_process_times(user_mode(regs));
       mark_bh(TIMER_BH);
}

而在內核2.6版本以後,定時器中斷處理採用了軟中斷機制而不是底半機制。時鐘中斷處理函數仍然為timer_interrup()-> do_timer_interrupt()-> do_timer_interrupt_hook()-> do_timer()。不過do_timer()函數的實現有所不同

void do_timer(struct pt_regs *regs)
{
       jiffies_64++;
       update_process_times(user_mode(regs));
       update_times();
}

更詳細的實現linux-2.6

Linux中斷處理之時鐘中斷(一)

(原創)linux內核進程調度以及定時器實現機制

進程管理與調度5 – 進程調度、進程切換原理詳解


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

-Advertisement-
Play Games
更多相關文章
  • 一、引入 基本上熟悉C#語言的沒有不知道Lambda表達式的,其對於數據的處理真的是太方便了。其中分組處理的GroupBy方法在List中的使用非常廣泛。正式近期一個功能需求中又遇到了,而且是需要GroupBy多個欄位的。現在功能已經完成,處理得到了想要數據,就順便來記錄一下,方便下次的查閱。 二、 ...
  • 在roadflow表單設計器不能滿足很複雜的業務需求的時候,可以採用自定義表單(即表單頁面自己做)。 自定義表單就是自己寫一個頁面,包含控制器視圖,然後將這個頁面掛到流程上進行審批。 自定義表單分為以下幾步: 1、自己寫頁面 示例: 控制器: 自定義表單的表單和數據保存代碼都是自己寫的,註意頁面一定 ...
  • ORM-SqlRepoEx 是 .Net平臺下相容.NET Standard 2.0,一個實現以Lambda表達式轉轉換標準SQL語句,使用強類型操作數據的輕量級ORM工具,在減少魔法字串同時,通過靈活的Lambda表達式組合,實現業務數據查詢的多樣性。 ...
  • 1、asp 的上傳 有兩個突出問題:1. 文件上傳框風格和整個頁面風格不搭配。 2. 上傳時是整個頁面回發,和FineUI預設的AJAX風格也不搭。 2、fineui 的上傳 下麵來看看FileUpload的屬性: 1. ButtonText:按鈕文本。 1. ButtonOnly:是否只顯示按鈕, ...
  • vs 打開項目報錯; 解決方法:刪除我的文檔下IISEXpress文件夾,然後重啟電腦。 ...
  • 按f1 搜索 Configore Display Language 設置 zh-cn 關閉軟體重啟。 如果重啟菜單等還是英文的,在商店查看已安裝的插件,把中文插件重新安裝一遍,然後重啟軟體。 ...
  • mknod 創建塊設備或者字元設備文件。此命令的適用範圍:RedHat、RHEL、Ubuntu、CentOS、SUSE、openSUSE、Fedora。 1、語法 mknod [選項] 設備名 設備類型 主設備號 次設備號 2、選項參數列表 選項 說明 --version 顯示命令版本信息 --he ...
  • 一、概述 記憶體管理涵蓋領域: 記憶體中的物理記憶體頁管理; 分配大塊記憶體的伙伴系統; 分配較小塊記憶體的slab、slub和slob分配器; 分配連續記憶體塊的vmalloc機制; 進程的地址空間。 Linux內核一般將處理器的虛擬地址分為兩個部分,以IA-32為例,地址空間在用戶進程和內核之間的劃分比例為 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...