Linux核心搶佔機制 - 簡介

來源:互聯網
上載者:User

Linux核心搶佔機制 - 簡介

本文主要圍繞 Linux 核心調度器 Preemption 的相關實現進行討論。其中涉及的一般作業系統和 x86 處理器和硬體概念,可能也適用於其它作業系統。

1. 背景知識

要深入理解 Preemption 必須對作業系統的 Context Switch 做一個全面的梳理。最終可以瞭解 Preemption 和 Context Switch 概念上的區別與聯絡。

1.1 Context Switch

Context Switch (環境切換) 指任何作業系統上下文儲存和恢複執行狀態,以便於被安全地打斷和稍後被正確地恢複執行。一般作業系統中通常因以下三種方式引起環境切換,

Task Scheduling (任務調度)

任務調度一般是由調度器代碼在核心空間完成的。
通常需要將當前 CPU 執行任務的代碼,使用者或核心棧,地址空間切換到下一個要運行任務的代碼使用者或核心棧地址空間

Interrupt (中斷) 或 Exception (異常)

中斷和異常是由硬體產生但由軟體來響應和處理的。
這個過程中,涉及到將使用者態或核心態代碼切換至中斷處理代碼。同時可能還涉及到使用者進程棧或核心棧切換到中斷棧。支援保護模式的處理器可能還涉及到保護模式的切換。x86 處理器是通過 Interrupt Gate (中斷門) 完成的。

System Call (系統調用)

系統調用是由使用者態代碼主動調用,使使用者進程陷入到核心態調用核心定義的各種系統調用服務。這個過程中,涉及到將任務的使用者態代碼和棧在同一任務上下文上切換至核心系統調用代碼和同一任務的核心棧

1.2 Preemption

Preemption (搶佔) 是指作業系統允許滿足某些重要條件(例如:優先順序,公平性)的任務打斷當前正在 CPU 上啟動並執行任務而得到調度執行。並且這種打斷不需要當前正在啟動並執行任務的配合,同時被打斷的程式可以在後來可以再次被調度恢複執行。

多任務作業系統可以按照 Cooperative Multitasking (協作多任務) 和 Preemptive Multitasking (搶佔式多任務) 來劃分。本質上,搶佔就是允許高優先順序的任務可以立即打斷低優先順序的任務而得到運行。對低 Scheduling Latency (調度延遲) 或者 Real Time (即時) 作業系統的需求來說,支援完全搶佔的特性是必須的。

三種環境切換方式中,系統調用始終發生在同一任務的上下文中,只有中斷異常任務調度機制才涉及到一個任務被令一個上下文打斷。Preemption 最終需要藉助任務調度來完成任務的打斷。但是,任務調度卻和這三種環境切換方式都密切相關,要理解 Preemption,必須對三種機制有深入的瞭解。

2. 任務調度

任務的調度需要核心代碼通過調用調度器核心的 schedule 函數引起。它主要完成以下工作,

完成任務調度所需的 Context Switch (環境切換) 調度演算法相關實現:選擇下一個要啟動並執行任務,任務運行狀態和 Run Queue (運行隊列) 的維護等

本文主要關註上下文切換和引起任務調度的原因。

2.1 任務調度環境切換

核心 schedule 函數其中一個重要的處理就是 Task Context Switch (任務環境切換)。調度器的任務環境切換主要做兩件事,

任務地址空間的環境切換。

在 Linux 上通過 switch_mm 函數完成。
x86 CPU 通過裝載下一個待啟動並執行任務的頁目錄位址 mm->pgd 到 CR3 寄存器來實現。

任務 CPU 運行狀態的環境切換。

主要是 CPU 各寄存器的切換,包括通用寄存器,浮點寄存器和系統寄存器的環境切換。

在 Linux x86 64位的實現裡,指令`CS:EIP` 和棧 `SS:ESP` 還有其它通用寄存器的切換由 switch_to 完成。Linux 描述任務的資料結構是 struct task_struct,其中的 thread 成員(struct thread_struct)用於儲存環境切換時任務的 CPU 狀態。

由於浮點寄存器環境切換代價比較大,而且,很多使用情境中,被調度的任務可能根本沒有使用過 FPU (浮點運算單元),所以 Linux 和很多其它 OS 都採用了 Lazy FPU 環境切換的設計。但隨著 Intel 今年來引入 XSAVE 特性來加速 FPU 儲存和恢複,Linux 核心在 3.7 引入了non-lazy FPU 環境切換。當核心檢測到 CPU 支援 XSAVE 指令集,就使用 non-lazy 方式。這也是Intel Intel 64 and IA-32 Architectures Software Developer’s Manual Volume 3的章節 13.4 DESIGNING OS FACILITIES FOR SAVING X87 FPU, SSE AND EXTENDED STATES ON TASK OR CONTEXT SWITCHES 裡建議的方式。

一般來說,任務調度,或者說任務環境切換,可以分為以下兩大方式來進行,

Voluntary Context Switch (主動環境切換) Involuntary Context Switch (強制環境切換)2.2 主動環境切換

主動環境切換就是任務主動通過直接或者間接調用 schedule 函數引起的環境切換。引起主動環境切換的常見時機有,

任務因為等待 IO 操作完成或者其它資源而阻塞。

任務顯式地調用 schedule 前,把任務運行態設定成TASK_UNINTERRUPTIBLE。保證任務阻塞後不能因訊號到來而引起睡眠過程的中斷,從而被喚醒。Linux 核心各種同步互斥原語,如 Mutex,Semaphore,wait_queue,R/W Semaphore,及其他各種引起阻塞的核心功能。

等待資源和特定事件的發生而主動睡眠。

任務顯式地調用 schedule 前,把任務運行態被設為 TASK_INTERRUPTIBLE。保證即使等待條件不滿足也可以被任務接收到的訊號所喚醒,重新進入運行態。Linux 核心各種同步互斥原語,如 Mutex,Semaphore,wait_queue,及其它各種引起睡眠的核心功能。

特殊目的,例如 debug 和 trace。

任務在顯式地用 schedule 函數前,利用 set_current_state 將任務設定成非 TASK_RUNNING 狀態。例如,設定成 TASK_STOPPED 狀態,然後調用 schedule 函數。

2.3 強制環境切換

強制環境切換是指並非任務自身意願調用 schedule 函數而引發的環境切換。從定義可以看出,強制環境切換的主要原因都和 Preemption 有關。

2.3.1 觸發 Preemption2.3.1.1 Tick Preemption

在周期性的時鐘中斷裡,核心調度器檢查當前正在運行任務的持續已耗用時間是否超出具體調度演算法支援的時間上限,從而決定是否剝奪當前任務的運行。一旦決定剝奪在 CPU 上任務的運行,則會給正在 CPU 上啟動並執行當前任務設定一個請求重新調度的標誌:TIF_NEED_RESCHED。

需要注意的是,TIF_NEED_RESCHED 標誌置位後,並沒有立即調用 schedule 函數發生環境切換。真正的環境切換動作是 User Preemption 或 Kernel Preemption 的程式碼完成的。

User Preemption 或 Kernel Preemption 在很多代碼路徑上放置了檢查當前任務的 TIF_NEED_RESCHED 標誌,並顯式調用 schedule 的邏輯。接下來很快就會有機會調用 schedule 來觸發任務切換,這時搶佔就真正的完成了。環境切換發生時,下一個被調度的任務將由具體調度器演算法來決定從運行隊列裡挑選。

例如,如果時鐘中斷剛好打斷正在使用者空間啟動並執行進程,那麼當 Tick Preemption 的代碼將當前被打斷的使用者進程的 TIF_NEED_RESCHED 標誌置位。隨後,時鐘中斷處理完成,並返回使用者空間。此時,User Preemption 的代碼會在中斷返回使用者空間時檢查 TIF_NEED_RESCHED 標誌,如果置位就會調用 schedule 來完成環境切換。

2.3.1.2 Wakeup Preemption

當原因需要喚醒另一個進程時,try_to_wake_up 的核心功能將會協助被喚醒的進程選擇一個 CPU 的 Run Queue,然後把進程插入到 Run Queue 裡,並設定成 TASK_RUNNING 狀態。這個過程中 CPU Run Queue 的選擇和 Run Queue 插入操作都是調用具體的調度演算法回呼函數來實現的。

任務插入到 Run Queue 後,調度器立即將新喚醒的任務和正在 CPU 上執行的任務交給具體的調度演算法去比較,決定是否剝奪當前任務的運行。與 Tick Preemption 一樣,一旦決定剝奪在 CPU 上執行的任務的運行,則會給當前任務設定一個 TIF_NEED_RESCHED 標誌。而實際的 schedule 調用並不是在這時完成的。但 Wakeup Preemption 在此處真正特殊的地方在於,執行喚醒操作的任務可能把被喚醒的任務插入到本地 CPU 的 Run Queue,但還可能插入到遠程 CPU 的 Run Queue。因此,try_to_wake_up 函數的調用根據被喚醒的任務將插入 Run Queue 所屬的 CPU 和執行喚醒任務正在啟動並執行 CPU 關係,分為如下兩種情況,

共用快取

被喚醒任務的目標 CPU 和當前運行喚醒 CPU 共用快取。

喚醒函數在返回過程中,只要當前任務運行到任何一處 User Preemption 或 Kernel Preemption 的代碼,這些代碼就會檢查到 TIF_NEED_RESCHED 標誌,並調用 schedule 的位置,環境切換才真正發生。實際上,如果 Kernel Preemption 是開啟的,在喚醒操作結束時的 spin_unlock 或者隨後的各種可能的中斷退出路徑都有 Kernel Preemption 調用 schedule 的時機。

不共用快取

被喚醒任務的目標 CPU 和當前運行喚醒 CPU 不共用快取。

這種情況下,喚醒操作在設定 TIF_NEED_RESCHED 標誌之後,會立即向被喚醒任務 Run Queue 所屬的 CPU 發送一個 IPI (處理器間中斷),然後才返回。以 Intel x86 架構為例,那個遠程 CPU 的 RESCHEDULE_VECTOR 被初始化來響應這個中斷,最終中斷處理函數 scheduler_ipi 在遠程 CPU 上執行。早期 Linux 核心,scheduler_ipi 其實是個空函數,因為所有中斷返回使用者空間或者核心空間都的出口位置都已經有 User Preemption 和 Kernel Preemption 的代碼在那裡,所以 schedule 一定會被調用。後來的 Linux 核心裡,又利用 scheduler_ipi 讓遠程 CPU 來做遠程喚醒的主要操作,從而減少 Run Queue 鎖競爭。所以現在的 scheduler_ipi 加入了新的代碼。

因 Wakeup Preemption 而導致的環境切換發生時,下一個被調度的任務將由具體調度器演算法來決定從運行隊列裡挑選。對於剛喚醒的任務,如果成功觸發了 Wakeup Preemption,則某些具體的調度演算法會給它一個優先被調度的機會。

2.3.2 執行 Preemption2.3.2.1 User Preemption

User Preemption 發生在如下兩種典型的狀況,

系統調用,中斷及異常在返回使用者空間前,檢查 CPU 當前正在啟動並執行任務的 TIF_NEED_RESCHED 標誌,如果置位則直接調用 schedule 函數。

任務為 TASK_RUNNING 狀態時,直接或間接地調用 schedule

舉個間接調用的例子:核心態的代碼在迴圈體內調用 cond_resched(),yield() 等核心 API,給其它任務得到調度的機會,防止獨佔濫用 CPU。

在核心態寫邏輯上造成長時間迴圈的代碼,有可能造成核心死結或者造成超長調度延遲,尤其是當 Kernel Preemption 沒有開啟時。這時可以在迴圈體內調用 cond_resched() 核心 API,有條件的讓出 CPU。這裡說的有條件是因為cond_resched 要檢查 TIF_NEED_RESCHED 標誌,看是否有新的 Preemption 的請求。而 yield 核心 API,不檢查 TIF_NEED_RESCHED 標誌,則無條件觸發任務切換,但在所在 CPU Run Queue 沒有其它任務的情況下,不會發生真正的任務切換。

2.3.2.2 Kernel Preemption

早期 Linux 核心只支援 User Preemption。2.6核心 Kernel Preemption 支援被引入。

Kernel Preemption 發生在以下幾種情況,

中斷,異常結束處理後,返回到核心空間時。

以 x86 為例,Linux 在中斷和異常處理代碼的公用代碼部分(即從具體 handler 代碼退出後),判斷是否返回核心空間,然後調用 preempt_schedule_irq 檢查 TIF_NEED_RESCHED 標誌,觸發任務切換。

禁止核心搶佔處理結束時

作為完全搶佔核心,Linux 只允許在當前核心上下文需要禁止搶佔的時候才使用 preempt_disable 禁止搶佔,核心代碼在禁止搶佔後,應該儘早調用 preempt_enable 使能搶佔,避免引入高調度延遲。為儘快處理在禁止搶佔期間 pending 的重新調度申請,核心在 preempt_enable 裡會調用 preempt_schedule 檢查 TIF_NEED_RESCHED 標誌,觸發任務切換。

使用 preempt_disable 和 preempt_enable 的核心上下文有很多,典型而又為人熟知的有各種核心鎖的實現,如 Spin Lock,Mutex,Semaphore,R/W Semaphore,RCU 等。

與 User Preemption 不同的是,上述兩種 Kernel Preemption 的情況發生時,任務的運行態可能已經被設定成 TASK_RUNNING 以外的睡眠狀態,如 TASK_UNINTERRUPTIBLE。此時接下來的核心 __schedule 代碼會有特殊處理,檢查 PREEMPT_ACTIVE 對上一個被 Preempt 的任務跳過移除隊列操作,保證 Kernel Preemption 儘快被處理。而 User Preemption 則不會在當前任務在 TASK_RUNNING 以外的狀態下發生,這是因為 User Preemption 總是發生在當前任務處於 TASK_RUNNING 的特殊位置。

3. 中斷和異常

Interrupt (中斷) 通常是由硬體或者是特殊軟體指令觸發的處理器需要立即響應的訊號。Exception (異常) 廣義上被歸類為中斷的一種。但狹義上,中斷和異常最大的區別是,中斷髮生和處理是非同步,但異常的發生和處理是同步的。

System Call 因為利用了特殊軟體指令給處理器產生了同步的 Trap (陷阱),也可被歸類為異常的一種。但由於其設計和用途和異常處理有明顯區別,將在另一個章節做單獨介紹。本節主要介紹中斷和異常。

3.1 中斷和異常的環境切換

很多英文技術文檔和討論裡把這種類型的打斷動作叫做 Pin,意思就是當前的任務沒有被切換走,而是被 Pin 住不能動彈了。這種打斷不像 Context Switch 那樣,涉及到地址空間的切換。而且這種打斷通暢和處理器和外圍硬體的中斷機制有關。依賴於不同作業系統的實現,可能中斷或者例外處理常式有自己的獨立核心棧,例如當前 Linux 版本在32位和64位 x86 上的實現;也可能使用任務當前的核心棧,例如早期 Linux 在32位 x86 上的實現。

以 Intel 的 x64 處理器為例,當外設產生中斷後,CPU 通過 Interrupt Gate (中斷門) 打斷了當前任務的執行。此時,不論正在執行的任務處於使用者態還是核心態,中斷門都會無條件儲存當前任務執行的寄存器執行內容。這些寄存器裡就有當前任務下一條待執行的程式碼片段指令寄存器 CS:RIP 和當前任務棧指標寄存器 SS:RSP。而新的中斷內容相關的程式碼片段指令寄存器 CS:RIP 的值,早由系統啟動時由 x86 的 IDT (中斷描述符表) 相關的初始化代碼設定為所有外設中斷的公用 IRQ Routine (中斷處理常式) 函數。在 Linux 3.19 的這個公用中斷處理常式 entry_64.S 的 irq_entries_start 彙編函數裡,SAVE_ARGS_IRQ 宏定義會把儲存在 per-CPU 變數 irq_stack_ptr 裡的核心 IRQ Stack (中斷棧) 賦值給 SS:RSP。這樣一來,一個完整的中斷環境切換就由 CPU 和中斷處理常式共同協作完成。中斷執行完畢,從中斷常式的返回過程則會利用之前儲存的上下文,恢複之前被打斷的任務。

x64 的異常機制與中斷機制類似,都利用了 IDT 來完被打斷任務的 CS:RIP 和 SS:RSP 的儲存,但 IDT 表裡異常的公用入口函數卻是不同的函數。而且在這個函數的彙編實現裡,切換到核心 IRQ Stack 的代碼是藉助硬體裡預先被核心初始化好的 IST (中斷服務表) 裡儲存的 SS:RSP 的值,這是與一般外設中斷處理的不同之處。

另外,x64 和 x32 的 IDT 機制在從核心態進入到中斷門時,硬體在是否把當前任務的 SS:RSP 寄存器壓棧的處理上有明顯差別。此外,IDT 在初始化時,IDT 描述符裡的 IST 選擇位如果非零,則意味著核心 IRQ Stack 的切換是要由核心代碼藉助 IST 實現。但如果 IDT 描述符的 IST 選擇位是零,則核心的 IRQ Stack 切換由核心代碼藉助 per-CPU 的核心中斷棧變數實現。

由於主題和篇幅限制,這裡不會詳細介紹中斷的環境切換機制。瞭解 x86 平台中斷和異常的環境切換機制,需要對 x86 處理器的硬體規範有所瞭解。Intel Intel 64 and IA-32 Architectures Software Developer’s Manual Volume 3裡的 6.14 EXCEPTION AND INTERRUPT HANDLING IN 64-BIT MODE 章節裡有硬體的詳細介紹,尤其詳細說明了32位和64位,以及中斷和異常的詳細差異。

3.2 中斷引起的任務調度

Linux 核心裡,中斷和異常因其打斷的上下文不同,在返回時可能會觸發以下類型的任務調度,

User Preemption

中斷和異常打斷了使用者態啟動並執行任務,在返回時檢查 TIF_NEED_RESCHED 標誌,決定是否調用 schedule。

Kernel Preemption

中斷和異常打斷了核心態啟動並執行任務,在返回時調用 preempt_schedule_irq。其代碼會檢查 TIF_NEED_RESCHED 標誌,決定是否調用 schedule。

User 和 Kernel Preemption 的代碼是實現在 Linux 核心所有中斷和異常處理函數通用處理代碼層的,因此,中斷異常的具體處理函數返回後就會被執行。儘管所有類型中斷都可能引發任務切換,和任務調度和搶佔密切相關,但以下兩種中斷直接與調度器相關,是核心調度器設計的一部分,

Timer Interrupt (時鐘中斷) Scheduler IPI (調度器處理器間中斷)3.2.1 時鐘中斷

Timer Interrupt (時鐘中斷) 對作業系統調度有著特殊的意義。
如前所述,周期性執行的時鐘中斷處理函數會觸發 Tick Preemption 的請求。隨後中斷在返回前,根據返回的上下文不同,可能會執行到 User Preemption 和 Kernel Preemption 的邏輯。這裡的中斷,可以是作業系統任何的中斷,例如一般外設的中斷。由於作業系統一般在具體中斷處理函數進入前和退出後有公用中斷處理邏輯,所以 Preemption 一般都實現在這裡,而具體的中斷處理函數並無 Preemption 的 Knowledge。而我們知道,外設中斷一般具有隨機性,所以,如果沒有時鐘中斷的存在,那麼 Preemption 的實現恐怕很難有時間保證了。因此,周期性的時鐘中斷在這裡發揮了重要的作用。當然,除了 Preemption,時鐘中斷還擔負了系統中很多重要的功能的處理,例如調度隊列的均衡,進程時間的更新,軟體定時器的執行等。下面從 Preemption 的角度簡單的討論一下與時鐘中斷的關係,

時鐘中斷源

核心的時鐘中斷是基於其運行硬體支援的可以周期觸發時鐘中斷的裝置來實現的。因此在不同硬體平台上,其實現機制和差異比較大。早期的 Linux x86 支援 PIT 還有 HPET 做時鐘中斷中斷源。現在 Linux 預設使用 x86 處理器的 Local APIC Timer 做時鐘中斷源。Local APIC Timer 與 PIT 和 HPET 最大的不同就是,APIC timer 中斷是 Per-CPU 的,但 PIT 和 HPET 是系統全域的。因此每 CPU 的 APIC Timer 中斷更加適合 SMP 系統的 Preemption 實現。

時鐘中斷頻率

早期 Linux 和一些 Unix 服務作業系統核心將時鐘中斷頻率設定成 100HZ。這意味著時鐘中斷的執行循環是 10ms。而新 Linux 核心預設將 x86 上 Linux 核心的頻率提高到 1000HZ。這樣,在 x86 上,時鐘中斷的處理周期縮短為 1ms。一個時鐘中斷周期通常被稱作一個 Tick。通常,Unix/Linux 都會使用一個全域技術器來對系統啟動以來的時鐘中斷次數來計數。Linux 核心中的這個全域變數被叫做 Jiffies。因此 Linux 核心中一個 Tick 也被叫一個 Jiffy。

當一個 Tick 從 10ms 縮短到 1ms,系統因處理高頻時鐘中斷的開銷理論上會增大,但著也帶來的更快更低延遲的 Preemption。由於硬體效能的提高,這種改變的負面影響很有限,但好處是很明顯的。

3.2.2 調度器處理器間中斷

Scheduler IPI (調度器處理器間中斷) 最初的引入主要是為瞭解決 SMP 系統中,喚醒代碼觸發 Wakeup Preemption 時,需要遠程 CPU 協助產生 User Preemption 或 Kernel Preemption 而引入的機制。其具體的過程如下,

喚醒代碼經過具體調度器演算法為被喚醒任務選擇 CPU。 當選擇的 CPU 是遠端時,將處於睡眠的進程喚醒並放入到遠程 CPU 所屬的 Run Queue 喚醒代碼調用具體調度演算法檢查是否觸發 Wakeup Preemption,並在返回前觸發 Scheduler IPI (調度器處理器間中斷)。 遠程 CPU 正在執行的代碼被打斷,Scheduler IPI 處理函數被執行。 Scheduler IPI 處理函數內部並無針對 Preemption 的實際處理。 Scheduler IPI 處理函數退出時會進入中斷處理的公用代碼部分,根據中斷返回上下文是使用者還是核心上下文,觸發 User Preemption 或 Kernel Preemption。

需要指出的是,新的 x86 平台和 Linux 核心裡,Timer Interrupt 和 Scheduler IPI 都屬於 CPU Local APIC 處理的中斷。在 Linux 核心裡,在 entry_64.S 裡的公用入口和返回都由 apicinterrupt 處理。而 apicinterrupt 和其它外設中斷的公用入口傳回碼共用 User Preemption 或 Kernel Preemption 的處理邏輯,即 ret_from_intr 的處理。

4. 系統調用

System Call (系統調用) 是為應用程式請求作業系統核心服務而設計的,一整套相對穩定的編程介面和服務常式。本小節主要關注系統調用的環境切換和引起的任務調度部分。

4.1 系統調用的環境切換

系統調用與中斷和異常最大的不同是,系統調用的發生是同步的,是應用程式通過編程介面主動觸發的,因此不存在打斷當前執行任務的作用。所以系統調用自身就是正在執行任務的一部分,只不過,它是為任務在核心空間執行代碼而已。因此,當任務調用系統調用主動陷入到核心執行系統調用代碼時,必然發生環境切換,一般來說,這個環境切換是由硬體來輔助完成的。

以 Intel x86 處理器為例,系統調用完成的使用者空間到核心空間的環境切換是由叫做 Trap Gate (陷阱門) 的硬體機制來實現的。Linux 作業系統支援以下兩種方式觸發 Intel x86 的陷阱門,

int 0x80 指令,較老的不支援系統調用指令的處理器使用。 sysenter 快速系統調用指令,較新的處理器支援。

陷阱門與中斷使用的中斷門類似,但其門調用發生過程中,沒有像中斷門一樣禁止中斷。當使用者態的代碼通過 glibc 的代碼發出上述指令觸發系統調用時,其環境切換按照如下步驟發生,

當前任務的代碼 CS:RIP 指向了陷阱門為系統調用向量早已初始化好的系統調用公用入口函數 CPU 陷阱門存自動儲存使用者任務的上下文,例如,系統調用號,使用者空間的代碼 CS:RIP 和使用者棧 SS:RSP 等。具體 layout 請參考硬體手冊。 系統調用公用入口代碼儲存其它寄存器上下文,最後將 SS:RSP 指向了該任務核心棧 struct thread_info 的地址,完成了任務使用者棧到核心棧的切換。 系統調用公用入口代碼做必要檢查後,調用全域的系統調用表,進入到具體系統調用的服務常式。4.2 系統調用引起的任務調度

與中斷處理類似,具體系統調用函數退出後,公用系統調用代碼返回使用者空間時,可能會觸發 User Preemption,即檢查 TIF_NEED_RESCHED 標誌,決定是否調用 schedule。系統調用不會觸發 Kernel Preemption,因為系統調用返回時,總是返回到使用者空間,這一點與中斷和異常有很大的不同。

5. 調度觸發時機總結

Linux 核心源碼 schedule 的注釋寫的非常精鍊,所以就不囉嗦了,直接上源碼,

/* * __schedule() is the main scheduler function. * * The main means of driving the scheduler and thus entering this function are: * *   1. Explicit blocking: mutex, semaphore, waitqueue, etc. * *   2. TIF_NEED_RESCHED flag is checked on interrupt and userspace return *      paths. For example, see arch/x86/entry_64.S. * *      To drive preemption between tasks, the scheduler sets the flag in timer *      interrupt handler scheduler_tick(). * *   3. Wakeups don't really cause entry into schedule(). They add a *      task to the run-queue and that's it. * *      Now, if the new task added to the run-queue preempts the current *      task, then the wakeup sets TIF_NEED_RESCHED and schedule() gets *      called on the nearest possible occasion: * *       - If the kernel is preemptible (CONFIG_PREEMPT=y): * *         - in syscall or exception context, at the next outmost *           preempt_enable(). (this might be as soon as the wake_up()'s *           spin_unlock()!) * *         - in IRQ context, return from interrupt-handler to *           preemptible context * *       - If the kernel is not preemptible (CONFIG_PREEMPT is not set) *         then at the next: * *          - cond_resched() call *          - explicit schedule() call *          - return from syscall or exception to user-space *          - return from interrupt-handler to user-space */
6. 關聯閱讀

本文主要介紹瞭解 Preemption 所需的基本概念,以及 Linux 核心是如何? User Preemption 和 Kernel Preemption 的。由於 Context Switch 與 Preemption 密切相關,所以也結合 Intel x86 處理器做了詳細分析。這些內容在很多 Linux 核心書籍也都有覆蓋,但要深入理解,還是需要結合某種處理器架構相關的知識來一起學習,否則很難深入理解。因此瞭解些硬體相關的知識是必要的。

Intel Intel 64 and IA-32 Architectures Software Developer’s Manual Volume 3 6.14 和 13.4 章節 x86 系統調用入門 Proper Locking Under a Preemptible Kernel Linux Kernel Stack

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.