0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

系統(tǒng)中的latency是如何產(chǎn)生的

Linux閱碼場 ? 來源:Linux閱碼場 ? 2024-06-04 09:18 ? 次閱讀

在當(dāng)今數(shù)字時代,手機已成為人們?nèi)粘I钪胁豢苫蛉?,多任?wù)處理和實時響應(yīng)對于用戶體驗越來越重要,搶占(preemption)機制在提升系統(tǒng)性能和用戶體驗方面發(fā)揮了至關(guān)重要的作用。內(nèi)核搶占機制使得系統(tǒng)能夠有效地管理多任務(wù)處理,確保系統(tǒng)對用戶操作的快速響應(yīng),并在資源緊張的情況下仍能保持穩(wěn)定和流暢的運行。

本篇文檔旨在詳細(xì)探討Linux內(nèi)核中的搶占機制,涵蓋其基本概念、實現(xiàn)細(xì)節(jié)、性能影響以及相關(guān)的調(diào)試方法。通過對搶占機制的深入解析,我們希望能夠幫助讀者更好地理解和優(yōu)化Linux系統(tǒng)的性能,并在實際工作應(yīng)用這些知識。

為了使內(nèi)容組織更清晰,本文檔將使用Linux6.1內(nèi)核,按照以下結(jié)構(gòu)展開:

1.基本概念:首先講解系統(tǒng)中的latency是如何產(chǎn)生的,為什么會產(chǎn)生

2.內(nèi)核搶占的實現(xiàn)機制:從latency的角度說明了為什么需要搶占,什么是搶占,內(nèi)核的搶占模型是怎么樣的,內(nèi)核中搶占點的設(shè)置、搶占計數(shù)的機制,以及如何在內(nèi)核代碼中控制搶占

3.Linux內(nèi)核中的搶占實現(xiàn):什么是內(nèi)核搶占和實現(xiàn)方式,包括在什么情況下會發(fā)生搶占以及搶占的觸發(fā)條件

4.實例分析:通過實際案例中一個高優(yōu)先級的線程長時間搶占不到資源,來看如何定位類似問題

1. latency in linux

內(nèi)核搶占允許高優(yōu)先級任務(wù)中斷正在執(zhí)行的低優(yōu)先級任務(wù),從而減少調(diào)度延遲,提高系統(tǒng)響應(yīng)性,那么我們就需要知道對于Linux內(nèi)核中有哪些因素會導(dǎo)致響應(yīng)不及時。首先,我們來看看典型應(yīng)用場景如下:5f266204-2206-11ef-91d2-92fbcf53809c.png

一個硬件中斷發(fā)生,并通過喚醒一個更高優(yōu)先級的任務(wù),一段時間后,高優(yōu)先級的任務(wù)才得以執(zhí)行,那么latency是實時響應(yīng)關(guān)注的重點。

接下來細(xì)化該過程,初始狀態(tài)時,進程處于睡眠等待,中斷發(fā)生,到喚醒這個進程,等待選核選到這個任務(wù),最后發(fā)生上下文切換,直到該進程運行,這個時間經(jīng)歷了以下幾個完整的完成,這個統(tǒng)稱為一次Scheduling latency。

5f72b014-2206-11ef-91d2-92fbcf53809c.png

這一次的內(nèi)核調(diào)度延遲 = 中斷延遲(interrupt latency) + 處理程序持續(xù)時間(handler duration) + 調(diào)度程序延遲(scheduler latency) + 調(diào)度程序持續(xù)時間(scheduler duration),每一個過程都會影響這個高優(yōu)先級任務(wù)的實時響應(yīng)。

1.1 中斷延遲

5f94cb22-2206-11ef-91d2-92fbcf53809c.png

在T0時刻,外設(shè)中斷發(fā)生,從中斷發(fā)生到linux內(nèi)核響應(yīng)這個中斷,之間有一個延時,稱為中斷延時,中斷延遲的來源主要有以下原因:

1. 內(nèi)核中大量使用了并發(fā)預(yù)防機制之一就是自旋鎖,并且這個是一個common的接口供所有的模塊使用,所以主要的來源就是中斷在內(nèi)核中被disable,如spinlock_irq()和spinlock_irq_irqsave(),此時當(dāng)中斷發(fā)生時候,內(nèi)核處于關(guān)中斷狀態(tài)。

2. 中斷控制器的調(diào)度延遲,現(xiàn)代的中斷控制器支持中斷優(yōu)先級調(diào)度,當(dāng)多個中斷同時發(fā)生時,內(nèi)核會通過比較中斷的優(yōu)先級來決定處理的順序。較高優(yōu)先級的中斷會被優(yōu)先處理,以保證對緊急事件的及時響應(yīng)。因此,它可能會被高優(yōu)先級中斷

3. 中斷處理會切換模式,保存寄存器狀態(tài)等,這個時間很短

4. Shared Interrupt Line:當(dāng)多個設(shè)備共享同一個中斷時,中斷控制器需要識別和區(qū)分不同的中斷源,這個能有效地減少系統(tǒng)中斷線的數(shù)量,節(jié)省硬件成本。然而,它也帶來了一些潛在的問題,其中之一就是中斷延遲的增加

1.2 中斷處理程序持續(xù)時間

5fb6e388-2206-11ef-91d2-92fbcf53809c.png

在T1時刻,CPU響應(yīng)了這個中斷,在Linux內(nèi)核的中斷處理分為上半部和下半部

1. 上半部分,它在禁用中斷的情況下運行,并且應(yīng)該盡快完成

2. 由上半部分調(diào)度的下半部分,在所有待處理的上半部分完成執(zhí)行后開始,下半部分是開中斷情況下執(zhí)行,可能被其他中斷打斷

如下圖,在處理完中斷A上部分后,其他外設(shè)中斷發(fā)生,CPU轉(zhuǎn)而處理其他中斷,這樣延遲處理中斷A下半部,我們把開始響應(yīng)中斷到這個處理的時間稱為中斷處理延遲,其處理的整個過程如下圖所示5fe1074e-2206-11ef-91d2-92fbcf53809c.png ? ?

對于前面兩個過程,我們在編寫外設(shè)驅(qū)動的時候,要特別注意,里面設(shè)計大量的關(guān)中斷和關(guān)搶占的過程,特別我們在一些自旋鎖以及變體的接口,使用不當(dāng)會導(dǎo)致響應(yīng)不及時,例如中斷響應(yīng)不及時,高優(yōu)先級任務(wù)遲遲得不到調(diào)度,給用戶的直接感受就是卡頓。

1.3 調(diào)度延遲

在T2時刻,中斷處理完后,喚醒了進程。從喚醒進程到進程被調(diào)度器選中的這段延時稱為調(diào)度延時。

5ffdd4c8-2206-11ef-91d2-92fbcf53809c.png

其產(chǎn)生調(diào)度延時的主要原因如下:

調(diào)度器選中進程A的時間也是不確定的,可能就緒隊列中有比進程A優(yōu)先級更高的進程

對于這個,就需要了解搶占,盡快的通過搶占來完成任務(wù)的切換工作。對于Linux內(nèi)核是一個支持搶占式操作系統(tǒng),當(dāng)一個任務(wù)運行在用戶空間并被中斷打斷時,如果中斷處理程序喚醒另外一個任務(wù),我們從中斷處理返回后可以立即調(diào)度該任務(wù)。對于不同內(nèi)核支持不同的搶占方式,處理方式也會不同,這個后面會詳細(xì)介紹,這里只是作為一個引子,目前存在以下情況會影響調(diào)度延遲

6024658e-2206-11ef-91d2-92fbcf53809c.png

當(dāng)中斷發(fā)生時,linux內(nèi)核正在自旋鎖臨界區(qū)里執(zhí)行,這樣,中斷完成后,不能馬上搶占調(diào)度,必須等待linux內(nèi)核執(zhí)行完自旋鎖臨界區(qū)才能搶占調(diào)度,這也會導(dǎo)致延遲的增加,并且很難被發(fā)現(xiàn)如下圖所示

60412d18-2206-11ef-91d2-92fbcf53809c.png

1.4 調(diào)度持續(xù)時間

在T3時刻,調(diào)度器選中了進程A,還需要進行上下文切換后才能執(zhí)行進程A,上下文切換也是具有一定的延時性

60d8824e-2206-11ef-91d2-92fbcf53809c.png ? ?

除了前面詳細(xì)講解的關(guān)鍵路徑之外,Linux的其他非確定性機制也會影響實時任務(wù)的執(zhí)行時間,例如linux是一個基于虛擬內(nèi)存,由MMU提供,因此內(nèi)存是按需分配的。每當(dāng)應(yīng)用程序首次訪問代碼和數(shù)據(jù)時,它都是按需加載的,這也會導(dǎo)致巨大的延時,同時C庫服務(wù)和內(nèi)核服務(wù)在設(shè)計的時候并未考慮實時約束。

1.5優(yōu)先級倒置

內(nèi)核搶占是指操作系統(tǒng)內(nèi)核能夠在某些情況下?lián)屨颊谶\行的任務(wù)并切換到更高優(yōu)先級的任務(wù)。但是在實際的場景中,可能會存在優(yōu)先級翻轉(zhuǎn)的問題導(dǎo)致系統(tǒng)響應(yīng)下降。

例如,低優(yōu)先級的進程可能持有高優(yōu)先級所需要的鎖,從而有效地降低該進程的優(yōu)先級,如果中等優(yōu)先級進程使用CPU,情況可能會更糟。在簡單的情況下,只要低優(yōu)先級任務(wù)(任務(wù) L)持有鎖,高優(yōu)先級任務(wù)(任務(wù) H)就會被阻塞。這被稱為“有界優(yōu)先級反轉(zhuǎn)”,因為反轉(zhuǎn)的時間長度受低優(yōu)先級任務(wù)在臨界區(qū)(持有鎖)中的時間長度的限制。

618efd80-2206-11ef-91d2-92fbcf53809c.png

當(dāng)中等優(yōu)先級任務(wù)(任務(wù) M)在持有鎖時中斷任務(wù) L 時,會發(fā)生無限優(yōu)先級反轉(zhuǎn)。之所以稱為“無界”,是因為任務(wù) M 現(xiàn)在可以有效地阻止任務(wù) H 任意時間,因為任務(wù) M 正在搶占任務(wù) L(它仍然持有鎖)。下面簡化了這種危險的事件序列,其過程如下:

61b47ea2-2206-11ef-91d2-92fbcf53809c.png

低優(yōu)先級任務(wù)L和高優(yōu)先級的任務(wù)H共享資源,在任務(wù)L獲取資源后不久,任務(wù)H就開始運行。但是任務(wù)H必須等待任務(wù)L完成資源,因此它被掛起

在任務(wù)L完成資源之前,任務(wù)M準(zhǔn)備好運行,搶占任務(wù)L,當(dāng)任務(wù)M(可能還有其他中等優(yōu)先級的任務(wù))運行時,系統(tǒng)中的最高優(yōu)先級任務(wù)H仍然處于掛起狀態(tài)。

2. 為什么需要內(nèi)核搶占

當(dāng)一個以優(yōu)先級為主的調(diào)度器中,當(dāng)一個新的進程(下圖中的task2)進入到可執(zhí)行(running)的狀態(tài),核心的調(diào)度器會檢查它的優(yōu)先級,若該進程的優(yōu)先權(quán)比目前正在執(zhí)行的進程(下圖中的task1)還高,核心調(diào)度器便會觸發(fā)搶占(preempt),使得正在執(zhí)行的進程被打斷,而擁有更高優(yōu)先級的進程會開始執(zhí)行。

61d3b466-2206-11ef-91d2-92fbcf53809c.png ? ?

在不支持內(nèi)核搶占模型中,搶占點比較少,對于內(nèi)核搶占,如右圖會在系統(tǒng)中添加很多搶占點,同時會導(dǎo)致執(zhí)行時間會比左圖多一點,可搶占會導(dǎo)致每隔一定時間去檢查是否需要搶占,這樣也會影響cache,pipeline,這樣就會犧牲吞吐量。從上面圖可以看出,操作系統(tǒng)演進過程中,不是新的就一定比舊的好,需要考量場景選擇合適的方案。從這張圖我們可以看出,內(nèi)核搶占主要解決以下問題:

提高系統(tǒng)響應(yīng)實時性和用戶體驗:在不支持內(nèi)核搶占式內(nèi)核中,低優(yōu)先級任務(wù)可能會長時間占用CPU,導(dǎo)致高優(yōu)先級任務(wù)無法及時得到處理,主要解決的是latency問題。這種情況會顯著影響系統(tǒng)的響應(yīng)速度,特別是在實時應(yīng)用中,可能導(dǎo)致嚴(yán)重的性能問題。對于手機場景中,當(dāng)用戶在使用應(yīng)用程序時,內(nèi)核搶占可以確保用戶界面關(guān)鍵線程得到足夠的CPU時間,避免界面卡頓和延遲。

避免優(yōu)先級翻轉(zhuǎn):內(nèi)核搶占結(jié)合優(yōu)先級繼承(Priority Inheritance)等機制,可以有效緩解優(yōu)先級翻轉(zhuǎn)問題。當(dāng)?shù)蛢?yōu)先級任務(wù)持有高優(yōu)先級任務(wù)需要的資源時,內(nèi)核搶占機制可以提高低優(yōu)先級任務(wù)的優(yōu)先級,使其盡快釋放資源,從而減少高優(yōu)先級任務(wù)的等待時間。在Linux中,從2.6開始,rtmutex支持優(yōu)先級繼承,解決優(yōu)先級翻轉(zhuǎn)的問題。

所以需要內(nèi)核搶占的根本原因就是系統(tǒng)在吞吐量和及時響應(yīng)之間進行權(quán)衡的結(jié)果,對于Linux作為一個通用的操作系統(tǒng),其最初設(shè)計就是為了throughput而非確定性時延而設(shè)計。但是越來越多的場景對及時響應(yīng)的要求越來越高,讓更高優(yōu)先級的任務(wù)和關(guān)鍵的任務(wù)及時得到調(diào)度,特別對于我們手機這種交互式的場景中。

3.搶占模型

將搶占視為減少調(diào)度程序延遲的一種方法可能很有用,但減少延遲通常也會影響吞吐量,因此需要在完成大量工作(高吞吐量)和在任務(wù)準(zhǔn)備好運行時立即調(diào)度任務(wù)(低延遲)之間保持平衡。Linux 內(nèi)核支持多種搶占模型,以便您可以根據(jù)工作負(fù)載調(diào)整搶占行為。為了讓用戶根據(jù)自己的需求進行配置,Linux 提供了 3 種 Preemption Model:

61f699ea-2206-11ef-91d2-92fbcf53809c.png

CONFIG_PREEMPT_NONE=y:不允許內(nèi)核搶占,吞吐量最大的 Model,一般用于 Server 系統(tǒng),其特點如下(紅色:non-preemptible,綠色:preemptible):

621b0a3c-2206-11ef-91d2-92fbcf53809c.png

該模式下只支持用戶搶占,系統(tǒng)調(diào)用返回和中斷是唯一的搶占點

CONFIG_PREEMPT_VOLUNTARY=y:內(nèi)核核心系統(tǒng)的開發(fā)者開始著手做低延遲優(yōu)化,其中一個優(yōu)化點就是如果有高優(yōu)先級進程需要處理器,內(nèi)核代碼也可以被搶占。在一些耗時較長的內(nèi)核代碼中主動調(diào)用cond_resched()讓出CPU,對吞吐量有輕微影響,但是系統(tǒng)響應(yīng)會稍微快一些。主動搶占(voluntary preemption)功能,它為內(nèi)核增加了一個受限的內(nèi)核搶占模式,并且一直使用到現(xiàn)在。

6233cbbc-2206-11ef-91d2-92fbcf53809c.png

通過向運行在內(nèi)核模式下的幾個代碼添加顯式搶占點,目前內(nèi)核中有近千個搶占點,檢查是否經(jīng)常需要重新調(diào)度,并且通過增加必須使用搶占的頻率,減少搶占延遲。

CONFIG_PREEMPT=y:除了處于持有 spinlock 時的 critical section,其他時候都允許內(nèi)核搶占,響應(yīng)速度進一步提升,吞吐量進一步下降,一般用于 Desktop / Embedded 系統(tǒng),目前Andorid中使用的這個配置項

6257181a-2206-11ef-91d2-92fbcf53809c.png ? ?

正如搶占選項名稱所暗示的那樣,這些設(shè)置中的每一項都有適當(dāng)?shù)挠美?。服?wù)器搶占可用于吞吐量是最重要的。另一方面,實時搶占應(yīng)該用在嵌入式系統(tǒng)中,其中絕對吞吐量并不重要,但最大體驗延遲才是關(guān)鍵。因此,Linux中不同的搶占級別可以在不同的環(huán)境中提供很大的靈活性

6272824e-2206-11ef-91d2-92fbcf53809c.png

另外,還有一個沒有合并進主線內(nèi)核的 Model: CONFIG_PREEMPT_RT,這個模式幾乎將所有的 spinlock 都換成了 preemptable mutex,只剩下一些極其核心的地方仍然用禁止搶占的 spinlock,所以基本可以認(rèn)為是隨時可被搶占,這部分不在本文討論的范圍之內(nèi)。

4. 什么是內(nèi)核搶占

說起這個搶占,在 Linux 內(nèi)核的 2.4 時代,除非主動調(diào)度schedule,否則通常只允許從 system call 或者 interrupt 返回用戶態(tài)的時候發(fā)生搶占(即產(chǎn)生中斷前,也在用戶態(tài)),這可稱之為 "User Preemption"。對于用戶搶占,只支持程序執(zhí)行在用戶態(tài)空間的時候,才可以被搶占,如果進程在Kernel空間執(zhí)行(系統(tǒng)調(diào)用),是不允許搶占的。其執(zhí)行過程如下:

6282a26e-2206-11ef-91d2-92fbcf53809c.png

如上圖一,假設(shè)周期性中斷發(fā)生在進程A用戶空間,此時進入到內(nèi)核空間,在周期性調(diào)度器實現(xiàn)函數(shù)中設(shè)置了進程A的TIF_NEED_RESCHED標(biāo)記位,則在時鐘中斷處理程序返回用戶空間前夕,將調(diào)用schedule()函數(shù)執(zhí)行進程調(diào)度

如上圖二,假設(shè)周期性中斷發(fā)生在進程A在內(nèi)核空間運行之時,時鐘中斷返回前并不會執(zhí)行進程調(diào)度,因為這時返回的是內(nèi)核空間,而不是用戶空間。在進程A從內(nèi)核空間返回用戶空間前的工作中,才會檢測TIF_NEED_RESCHED標(biāo)記位,置位則執(zhí)行進程調(diào)度

何為內(nèi)核搶占?簡單地說就是當(dāng)進程進入內(nèi)核空間運行時,能否被搶占,被剝奪CPU控制權(quán),執(zhí)行進程調(diào)度,從而運行其它進程。還是以中斷和異常為例,對比其差異

62a6e516-2206-11ef-91d2-92fbcf53809c.png

如上圖一,假設(shè)周期時鐘中斷發(fā)生在進程A用戶空間,此時的處理與不支持內(nèi)核搶占時相同,在中斷處理程序返回用戶空間前夕的工作中,執(zhí)行進程調(diào)度。

如上圖二,假設(shè)周期時鐘中斷發(fā)生在進程A在內(nèi)核空間運行時,在時鐘中斷處理程序返回內(nèi)核空間前的工作中就可能會執(zhí)行進程調(diào)度(搶占計數(shù)需為0)。如果在中斷處理程序返回內(nèi)核空間前沒有執(zhí)行進程調(diào)度,則在返回用戶空間前執(zhí)行,與不支持內(nèi)核搶占時相同。

5.Linux搶占標(biāo)志位--TIF_NEED_RESCHED

首先,我們從數(shù)據(jù)結(jié)構(gòu)開始,我們會詳細(xì)探討thread_info數(shù)據(jù)結(jié)構(gòu)和它在Linux搶占中的作用和關(guān)系

62be576e-2206-11ef-91d2-92fbcf53809c.png

這個數(shù)據(jù)結(jié)構(gòu)與搶占的發(fā)展歷程也有關(guān)系,其提供功能如下:

調(diào)度標(biāo)志位設(shè)置:早期的Linux,只需要調(diào)用set_tsk_need_resched 給當(dāng)前任務(wù)設(shè)置 struct thread_info 的 TIF_NEED_RESCHED 標(biāo)志,所以就提供了一個thread_info的flags中有一個是TIF_NEED_RESCHED,后面會詳細(xì)介紹

搶占計數(shù):為了實現(xiàn)內(nèi)核搶占,新加入preempt_count,這筆提交請參考arm64: preempt: Provide our own implementation of asm/preempt.h,可以發(fā)現(xiàn)它是一個共用體,內(nèi)核某些路徑使用preempt_count,有的是preempt,為何會使用這么奇怪的定義呢?后面將詳細(xì)揭曉答案

內(nèi)核如何檢查一個進程是否需要被調(diào)度呢?早期的Linux,在即將返回用戶空間時,檢查進程是否需要重新調(diào)度,如果設(shè)置了,就會發(fā)生調(diào)度,內(nèi)核主要是在thread_info的flag重設(shè)置標(biāo)識來標(biāo)記進程是否需要被調(diào)度,即重新調(diào)度need_resched標(biāo)識TI_NEED_RESCHED,其主要的接口函數(shù)為

62d1aa76-2206-11ef-91d2-92fbcf53809c.png

當(dāng)內(nèi)核的某個路徑設(shè)置重新調(diào)度標(biāo)志(如時鐘中斷tick 時),會調(diào)用到resched_curr 來設(shè)置重新調(diào)度標(biāo)志:可以看到除了設(shè)置任務(wù)的flags 的TIF_NEED_RESCHED 標(biāo)志外,還設(shè)置了preempt.need_resched 為0

62ef3e56-2206-11ef-91d2-92fbcf53809c.png

清搶占標(biāo)志,__schedule 中pick到下一個任務(wù)后會清除搶占標(biāo)志,其代碼實現(xiàn)為:

6303b250-2206-11ef-91d2-92fbcf53809c.png

6. 搶占計數(shù)preempt_count

在像Linux這樣的多任務(wù)系統(tǒng)中,任何執(zhí)行線程都不能保證只要它想運行就可以獨占訪問處理器。內(nèi)核總是有能力(多數(shù)情況下)搶占一個正在運行的線程,而選擇一個優(yōu)先級更高的線程來執(zhí)行。新線程可能是另一個不同的進程,但也可能是一個硬件中斷,或者其他外部事件。

為了正確協(xié)調(diào)系統(tǒng)中所有任務(wù)能正確運行,內(nèi)核必須跟蹤當(dāng)前的執(zhí)行狀態(tài),包括已經(jīng)被搶占或可能阻止線程被搶占的各種情況。用來進行這個追蹤記錄的基礎(chǔ),就是在系統(tǒng)中每個任務(wù)里存儲的 preemption counter。這個計數(shù)器是通過 preempt_count() 函數(shù)來訪問的,它的通用定義是這樣的:

6325ed20-2206-11ef-91d2-92fbcf53809c.png

這個 counter 可以用來指示當(dāng)前線程的狀態(tài)、它是否可以被搶占,以及它是否被允許睡眠。要實現(xiàn)這個功能的話,就必須在這個 counter 里面記錄若干種不同狀態(tài),因此這個 preempt_count 也被分成了幾個字段(sub-fields):

6339d6c8-2206-11ef-91d2-92fbcf53809c.png

最低位的這個 byte 是用來記錄 preempt_disable()嵌套調(diào)用的次數(shù),也就是到目前為止 preemption 被 disable 的次數(shù)。

SOFTIRQ:當(dāng)CPU進入軟中處理程序時,對該位域加1,退出時減1

HARDIRQ:當(dāng)CPU進入硬件中斷處理函數(shù)時,對該位域加1,退出時減1,位域數(shù)值表示中斷嵌套層級

NMI:CPU進入不可屏蔽中斷處理函數(shù)時,此位置1,退出時清0。

最后,最高位表示內(nèi)核是否已經(jīng)決定當(dāng)前進程需要在后面執(zhí)行時一有機會就馬上被調(diào)度出去,讓給其他任務(wù)。

接下來,我們看看內(nèi)核是如何定義這塊的,其定義在include/linux/preempt.h

6384ceda-2206-11ef-91d2-92fbcf53809c.png

其每個bit的定義如下:

63a75cfc-2206-11ef-91d2-92fbcf53809c.png

這里需要特別注意,preempt_count是允許嵌套的,在進入臨界區(qū),被中斷打斷,軟中斷都會存在preempt_count。下圖展示了preempt_count相關(guān)的操作函數(shù)

63c0b576-2206-11ef-91d2-92fbcf53809c.png

這里要特別注意irq這個,它包含了NMI、IRQ和SOFTIRQ63e3f0c2-2206-11ef-91d2-92fbcf53809c.png ? ?

下圖是preempt_count相關(guān)的條件判斷函數(shù),這個在搶占中會頻繁用到

64063d8a-2206-11ef-91d2-92fbcf53809c.png

以下接口函數(shù)用于檢測preempt_count成員相應(yīng)位域值,用于檢測CPU是否處于硬件中斷、軟中斷等處理函數(shù)中(include/linux/preempt.h):

in_irq():當(dāng)前CPU是否處于硬件中斷處理程序內(nèi),返回HARDIRQ計數(shù)值。

in_softirq():當(dāng)前CPU是否處于軟中斷處理函數(shù)內(nèi)或禁止軟中斷,返回SOFTIRQ計數(shù)值。

in_serving_softirq():當(dāng)前CPU是否處于軟中斷處理函數(shù)內(nèi)。

in_nmi():CPU是否處于不可屏蔽中斷處理程序內(nèi),返回NMI計數(shù)值。

in_interrupt():CPU是否處于中斷處理程序內(nèi)(或禁止軟中斷狀態(tài)),包括硬件中斷、軟中斷和不可屏蔽中斷。

64232544-2206-11ef-91d2-92fbcf53809c.png

只要看一下 preempt_count 的值,內(nèi)核就可知道當(dāng)前的情況如何。比如,preempt_count 是非零值,就表示當(dāng)前線程不能被 scheduler 搶占:要么是 preemption 已經(jīng)被明確 disable 了,要么是 CPU 當(dāng)前正在處理某種中斷。

同理,非零值也表示當(dāng)前線程不能睡眠,因為它此刻在運行的上下文必須要持續(xù)執(zhí)行完成。"reschedule needed" 這個 bit 告訴內(nèi)核,當(dāng)前有一個優(yōu)先級較高的進程應(yīng)該在第一時間獲得 CPU。必須要在 preempt_count 為非零值的情況下,才會設(shè)置這個 bit。否則的話,內(nèi)核早就可以直接對這個進程進行 prempt 搶占,而不是設(shè)置此 bit 并等待。

那么哪些情況下,會操作preempt_count,下面是preempt_count相關(guān)操作函數(shù)

643de33e-2206-11ef-91d2-92fbcf53809c.png ? ?

對于這些接口以及相關(guān)變體,都是內(nèi)核中通用的接口API,所以系統(tǒng)實時性會受驅(qū)動中如何使用這些接口的影響。這里我們來看看經(jīng)常討論的中斷上下文、進程上下文和atomic上下文的關(guān)系,首先我們來看看代碼實現(xiàn):

64524612-2206-11ef-91d2-92fbcf53809c.png

所以總結(jié)一下,對于內(nèi)核什么時候不允許搶占,在哪些時機是不可調(diào)度的,想要搞清這個問題,首先需要介紹一下linux中的四類區(qū)間:

中斷

軟中斷

進程上下文中的spin_lock

進程上下文中的其他區(qū)域

上述四類區(qū)間中,只有第四類區(qū)間支持搶占調(diào)度,對于1,2,3也就是atomic上下文。當(dāng)可以調(diào)度的事情發(fā)生在前3類區(qū)間中,即如果在這3類區(qū)間中喚醒了高優(yōu)先級的可以搶占的task,實際上卻不能搶占,直到這3類區(qū)間結(jié)束。那么對于4類區(qū)間是不是一定能發(fā)生搶占呢?

7. preempt_enable

為了支持內(nèi)核搶占 而引入了 preempt_count ,如果為 0,就允許 Kernel Preemption,否則就不允許。內(nèi)核函數(shù)preempt_enable/preempt_disable用來內(nèi)核代碼臨界區(qū)動態(tài)關(guān)閉和打開內(nèi)核搶占,詳細(xì)的用法請參考preempt-locking。

內(nèi)核代碼中preempt_disable()和preempt_enable()函數(shù)總是成對出現(xiàn)的,用于保證進程在執(zhí)行這兩個函數(shù)之間的代碼時,不會發(fā)生進程調(diào)度(當(dāng)前進程不會被搶占,不被搶占不是說不能被中斷,硬件中斷還是允許的,只是中斷還是返回原進程)

preempt_disable()函數(shù)用于禁止內(nèi)核搶占,函數(shù)定義如下(include/linux/preempt.h)

6472c996-2206-11ef-91d2-92fbcf53809c.png

這個比較簡單,preempt_count加一,然后做了一個內(nèi)存屏障,增加搶占計數(shù)器以防止重新調(diào)度,不管處于哪種搶占模式,都不允許搶占。

內(nèi)核搶占函數(shù)preempt_enable()定義在include/linux/preempt.h頭文件內(nèi):

64870384-2206-11ef-91d2-92fbcf53809c.png

preempt_enable()函數(shù)內(nèi)對搶占計數(shù)值減1,如果減1后為0,并且進程TIF_NEED_RESCHED標(biāo)記置位了,則調(diào)用__preempt_schedule()函數(shù)執(zhí)行進程調(diào)度(搶占當(dāng)前進程)。

649ee904-2206-11ef-91d2-92fbcf53809c.png

對于ARM64,使用64位的preempt_count,通過將其劃分為count和need_resched來管理,判斷 preempt_count 和 TIF_NEED_RESCHED 看是否可以被搶占

64b487be-2206-11ef-91d2-92fbcf53809c.png ? ?

這個首先來檢查若當(dāng)前CPU處于關(guān)中斷狀態(tài)和preempt_count不為0,就禁止搶占,反之就執(zhí)行搶占。

內(nèi)核代碼里面通常直接調(diào)用preempt_enable比較少,但是調(diào)用鎖的地方比較多,例如常見的spinlock等,目前內(nèi)核的這種鎖機制又是一個處于泛濫的趨勢,所以可以認(rèn)為每次調(diào)用spinlock結(jié)束時默認(rèn)都會發(fā)起一次隱式搶占

64cb6614-2206-11ef-91d2-92fbcf53809c.png

8. Linux內(nèi)核中的搶占實現(xiàn)

在當(dāng)前進程被搶占的場景下,調(diào)度并不是立刻發(fā)生,而是延遲執(zhí)行,具體的方法是設(shè)定當(dāng)前進程的need_resched等于1,然后靜靜的等待最近一個調(diào)度點的來臨,當(dāng)調(diào)度點到來的時候,內(nèi)核會調(diào)用schedule函數(shù),搶占當(dāng)前task的執(zhí)行。這部分的內(nèi)容比較多,有興趣的同學(xué)可以自行查看源碼,大致梳理了一個相關(guān)知識的導(dǎo)圖。

64ea5ede-2206-11ef-91d2-92fbcf53809c.png

9. 案例分析

對于性能開發(fā)的同學(xué),經(jīng)常會遇到這種runnable很長的問題,那么我們以下面這個為例,crtc_commit的RT線程長時間runnable,為什么沒搶占cfs的線程

6502a07a-2206-11ef-91d2-92fbcf53809c.png

首先,我們來看看結(jié)合梳理下整個流程是如何的,有什么影響因素,關(guān)鍵問題卡在哪個環(huán)節(jié)

65211e06-2206-11ef-91d2-92fbcf53809c.png

結(jié)合目前的ftrace下相關(guān)tracepoint,大致就可以有一個分析問題的思路

65423bf4-2206-11ef-91d2-92fbcf53809c.png ? ?

首先從日志來看,當(dāng)喚醒的時刻,這個crtc_commit線程會發(fā)生選核,從選核邏輯上看,這個線程選擇了cpu1,而后差不多6ms后被做了loadbalance遷移到cpu4上

6565466c-2206-11ef-91d2-92fbcf53809c.png

為什么會出現(xiàn)在選核完成后,沒有第一時間內(nèi)搶占cpu1上的HwBinder:1844_1這個線程,為什么沒有發(fā)生正常的一次調(diào)度?如何看這個問題?還有這個線程為什么能運行這么久?

目前對于內(nèi)核ftrace提供分析的方法

6594455c-2206-11ef-91d2-92fbcf53809c.png

這個代碼的實現(xiàn)如下,詳細(xì)的可以參考代碼和ftrace.txt

65b4f388-2206-11ef-91d2-92fbcf53809c.png

那我們就可以通過這個方法來看看,這個HwBinder:1844_1在選核的時候發(fā)生了什么情況?可以看到這個時候中斷被關(guān)閉了,同時preempt也被disable了

65d1e04c-2206-11ef-91d2-92fbcf53809c.png

同時arch定時器中斷也有延遲,通常至少需要 4ms 會出現(xiàn) arch 定時器中斷,而出現(xiàn)問題這段時間內(nèi),系統(tǒng)arch_timer也出現(xiàn)問題65f5084c-2206-11ef-91d2-92fbcf53809c.png

下一步就需要去排查驅(qū)動中是哪里會關(guān)這么長時間的中斷,可以開啟preemptirq和preemptirq_long相關(guān)的tracepoint進行復(fù)現(xiàn)debug,所以在寫內(nèi)核代碼的時候,需要關(guān)注preempt_count相關(guān)操作函數(shù)及其變體函數(shù),這個會切身影響到系統(tǒng)的實時性。

10. 總結(jié)

本文檔主要探討了Linux 6.1內(nèi)核中搶占特性的原理和實現(xiàn),重點關(guān)注latency產(chǎn)生原因、內(nèi)核搶占模型與機制以及實例分析,而目前遇到的痛點問題是搶占造成資源競爭,以及鎖和中斷延遲對于實時性的影響,特別目前鎖是一個通用的API接口,任何驅(qū)動都可以隨便使用,導(dǎo)致得不到及時搶占。

Linux內(nèi)核的搶占機制與中斷、鎖機制之間的矛盾是提高系統(tǒng)實時性和系統(tǒng)優(yōu)化的的一大挑戰(zhàn),也希望PREEMPT_RT的實時補丁能盡快合進內(nèi)核主線,增強了Linux內(nèi)核的實時性能,通過減少不可搶占的臨界區(qū)和優(yōu)化中斷處理來提高搶占性。

審核編輯:彭菁

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11123

    瀏覽量

    207889
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4235

    瀏覽量

    61962
  • 模型
    +關(guān)注

    關(guān)注

    1

    文章

    3031

    瀏覽量

    48346
  • API接口
    +關(guān)注

    關(guān)注

    1

    文章

    81

    瀏覽量

    10398

原文標(biāo)題:全方位剖析內(nèi)核搶占機制

文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    在計算指令周期時(Delay_Slot),其是否包括了功能單元的延時時間(Function Uint Latency)?

    的延時時間(Function Uint Latency)是不包括在指令周期時(Delay_Slot)的。因而總的執(zhí)行時間是:4 cycles ? 而對于指令:MPYDP,在手冊上給出
    發(fā)表于 06-21 15:25

    [DM385] Latency Performance

    Dear all, ?我的範(fàn)例是 Capture -> swMs -> Display (1080p@60) 我想知道每一級的Latency, 有DM385的相關(guān)資料?目前我查到的資料如下連結(jié)
    發(fā)表于 06-21 05:15

    嵌入式系統(tǒng)EMC的產(chǎn)生原理是什么?

    嵌入式系統(tǒng)EMC(Electro Magnetic Compatibility)即嵌入式系統(tǒng)電磁兼容性,指嵌入式系統(tǒng)在復(fù)雜電磁環(huán)境抵抗其他系統(tǒng)
    發(fā)表于 08-20 08:08

    什么是諧波_電力系統(tǒng)諧波產(chǎn)生的原因和危害

      一、諧波  1. 何為諧波?  在電力系統(tǒng)諧波產(chǎn)生的根本原因是由于非線性負(fù)載所致。當(dāng)電流流經(jīng)負(fù)載時,與所加的電壓不呈線性關(guān)系,就形成非正弦電流,即電路中有諧波產(chǎn)生。諧波頻率是基波
    發(fā)表于 06-30 16:02

    請問修改DEFAULT_DESIRED_SLAVE_LATENCY 值?

    請教一下,我把DEFAULT_DESIRED_SLAVE_LATENCY 的值修改為5:// Minimum connection interval (units of 1.25ms, 80
    發(fā)表于 08-27 06:08

    Latency TestUAC設(shè)備的相關(guān)資料分享

    https://manual.audacityteam.org/man/latency_test.html - Latency TestUAC設(shè)備2021/09/12輸出??!
    發(fā)表于 12-21 06:10

    TMOS設(shè)置DEFAULT_DESIRED_SLAVE_LATENCY會不會造成什么不良后果?

    你好,我的工程中有一些任務(wù)比較長。我也知道TMOS不做搶占,任務(wù)太長會影響藍(lán)牙。同時“TMOS使用說明”也提到了:1、建議不要在單個任務(wù)執(zhí)行超過連接間隔一半時長的任務(wù),否則將影響藍(lán)牙通訊2、同理
    發(fā)表于 08-25 07:43

    i.MX8MP EQOS MAC_Ingress_Timestamp_Latency和MAC_Egress_Timestamp_Latency始終為0的原因?

    和 11.7.2.5.3.2 節(jié)),但我總是閱讀0 來自 MAC_[Ingress,Egress]_Timestamp_Latency 寄存器。這是配置問題嗎?
    發(fā)表于 04-03 07:15

    一種可用于相關(guān)檢測系統(tǒng)的波門產(chǎn)生電路

    一種可用于相關(guān)檢測系統(tǒng)的波門產(chǎn)生電路
    發(fā)表于 02-07 16:14 ?2次下載

    JESD204B SystemC module Deterministic Latency(四)

    1Deterministic Latency 很多JESD204的系統(tǒng)包含多種多樣的數(shù)據(jù)處理單元,并且他們處于不同的時鐘域中,所以將導(dǎo)致無法確定的延遲。這些延遲將在鏈路層上電、斷電、復(fù)位時產(chǎn)生隨機
    發(fā)表于 02-08 10:39 ?1197次閱讀
    JESD204B SystemC module Deterministic <b class='flag-5'>Latency</b>(四)

    Zero Latency與微軟、惠普和英特爾合作 共同打造下一代VR娛樂平臺

    最近,VR主題公園運營商Zero Latency宣布與微軟、惠普和英特爾合作,共同打造下一代VR娛樂平臺。
    發(fā)表于 01-28 16:30 ?997次閱讀

    Zero Latency VR與育碧合作 共同打造沉浸式VR游戲

    近日,Zero Latency VR宣布與育碧合作,打造全新的自由漫游VR游戲。游戲目前正在開發(fā),預(yù)計將于2021年發(fā)布,在Zero Latency VR全球場館獨家發(fā)售。
    的頭像 發(fā)表于 06-27 15:34 ?1910次閱讀

    Low Latency High Bandwidth Memory 數(shù)據(jù)表(Digest Edition)

    Low Latency High Bandwidth Memory 數(shù)據(jù)表 (Digest Edition)
    發(fā)表于 03-15 20:26 ?0次下載
    Low <b class='flag-5'>Latency</b> High Bandwidth Memory 數(shù)據(jù)表(Digest Edition)

    時序分析基本概念介紹&lt;Latency&gt;

    今天要介紹的時序分析基本概念是Latency, 時鐘傳播延遲。主要指從Clock源到時序組件Clock輸入端的延遲時間。
    的頭像 發(fā)表于 07-04 15:37 ?2080次閱讀
    時序分析基本概念介紹&lt;<b class='flag-5'>Latency</b>&gt;

    Low Latency High Bandwidth Memory 數(shù)據(jù)表(Digest Edition)

    Low Latency High Bandwidth Memory 數(shù)據(jù)表 (Digest Edition)
    發(fā)表于 07-06 19:37 ?0次下載
    Low <b class='flag-5'>Latency</b> High Bandwidth Memory 數(shù)據(jù)表(Digest Edition)