1. 程式人生 > >第8章 下半部和推後執行的工作

第8章 下半部和推後執行的工作

核心為處理中斷而提供的中斷處理程式機制。中斷處理程式是核心中很有用的部分。但是,由於本身存在一些侷限,所以它只能完成整個中斷處理流程的上半部分。這些侷限包括:

中斷處理程式以非同步方式執行,並且有可能會打斷其他重要程式碼的執行。因此,為了避免被打斷的程式碼停止時間過長,中斷處理程式應該執行越快越好。

如果當前有一箇中斷處理程式正在執行,在最好的情況下(如果設定了IRQF_DISABLED),與該中斷同級的其他中斷會被遮蔽,在最壞的情況下(如果設定了IRQF_DISABLED),當前處理器上所有其他中斷都會被遮蔽。因為禁止中斷後硬體與作業系統無法通訊,因此,中斷處理程式執行得越快越好。

由於中斷處理程式需要對硬體進行操作,所以它們通常有很高的時限要求。

中斷處理程式不在程序上下文中執行,所以它們不能阻塞。

中斷處理程式只能作為整個硬體中斷處理流程的一部分。作業系統必須有一個快速、非同步、簡單的機制負責對硬體做出迅速響應並完成時間要求很嚴格的操作。中斷處理程式適合於實現這些功能,可是,對於那些其他的、對時間要求相對寬鬆的任務,就應該推後到中斷被啟用以後再去執行。

這樣,整個中斷處理流程就被分為了兩個部分。第一個部分是中斷處理程式(上半部),核心通過對它的非同步執行完成對硬體中斷的即時響應。下面研究中斷處理流程中的另外那一部分,下半部。

8.1 下半部

下半部的任務就是執行與中斷處理密切相關但中斷處理程式本身不執行的工作。在理想的情況下,最好是中斷處理程式將所有工作都交給下半部分執行,因為希望在中斷處理程式中完成的工作越少越好。期望中斷處理程式能夠儘可能快地返回。

但是,中斷處理程式要完成一部分工作。例如,中斷處理程式幾乎都需要通過操作硬體對中斷的到達進行確認,有時它還會從硬體拷貝資料。因為這些工作對時間非常敏感,所以只能靠中斷處理程式自己去完成。

剩下的幾乎所有其他工作都是下半部執行的目標。例如,如果在上半部中把資料從硬體拷貝到了記憶體,那麼當然應該在下半部中處理它們。並不存在嚴格明確的規定來說明到底什麼任務應該在哪個部分中完成——如何做決定完全取決於驅動程式開發者自己的判斷。儘管在理論上不存在什麼錯誤,但輕率的實現效果往往不很理想。記住,中斷處理程式會非同步執行,並且在最好的情況下它也會鎖定當前的中斷線。因此將中斷處理程式持續執行的時間縮短到最小程度顯得非常重要。對於在上半部和下半部之間劃分工作,儘管不存在某種嚴格的規則,但還是有一些提示可供借鑑:

如果一個任務對時間非常敏感,將其放在中斷處理程式中執行。

如果一個任務和硬體有關,將其放在中斷處理程式中執行。

如果一個任務要保證不被其他中斷打斷,將其放在中斷處理程式中執行。

其他所有任務,考慮放置在下半部執行。

通常,中斷處理程式要執行得越快越好。

8.1.1 為什麼要用下半部

理解為什麼要讓工作推後執行以及在什麼時候推後執行非常關鍵。希望儘量減少中斷處理程式中需要完成的工作量,因為它在執行時,當前的中斷線在所有處理器上都會被遮蔽。更糟糕的是,如果一個處理程式是IRQF_DISABLED型別,它執行時會禁止所有本地中斷。而縮短中斷被遮蔽的時間對系統的響應能力和效能都至關重要。再加上中斷處理程式要與其他程式非同步執行,所以很明顯,必須盡力縮短中斷處理程式的執行。解決的方法就是把一些工作放到以後去做。

但具體放到以後什麼時候去做呢?理解這一點相當重要。下半部並不需要指明一個確切時間,只要把這些任務推遲一點,在系統不太繁忙併且中斷恢復後執行就可以了。通常下半部在中斷處理程式一返回就會馬上執行。下半部執行的關鍵在於當它們執行的時候,執行響應所有的中斷。

不僅僅是Linux,許多作業系統也把處理硬體中斷的過程分為兩個部分。上半部分簡單快速,執行的時候禁止一些或者全部中斷。下半部分稍後執行,而且執行期間可以響應所有的中斷。這種設計可使系統處於中斷遮蔽狀態的時間儘可能的短,依次來提高系統的響應能力。

8.1.2 下半部的環境

和上半部只能通過中斷處理程式實現不同,下半部可通過多種機制實現。這些用來實現下半部的機制分別由不同的介面和子系統組成。實現中斷處理程式的方法只有一種,實現一個下半部會有許多不同的方法。

本章討論2.6版本的核心中的下半部機制是如何設計和實現的。

1、下半部的起源

最早的Linux只提供“bottom half”這種機制用於實現下半部。這種機制也被稱為“BH”,BH介面也非常簡單。它提供了一個靜態建立、由32個bottom halves組成的連結串列。上半部通過一個32位整數中的一位來標識出哪個bottom half可以執行。每個BH都在全域性範圍內進行同步。即使分屬於不同的處理器,也不允許兩個bottom half同時執行。這種機制使用方便卻不夠靈活,簡單卻有效能瓶頸。

2、任務佇列

核心開發者引入任務佇列機制來實現工作的推後執行,並用它來代替BH機制。核心為此定義了一組佇列,其中每個佇列都包含一個由等待呼叫的函式組成連結串列。根據其所處佇列的位置,這些函式會在某個時刻執行。驅動程式可以把它們自己的下半部註冊到合適的佇列上去。這種機制表現得還不錯,但仍不夠靈活,無法代替整個BH介面。對於一些效能要求較高的子系統,例如網路部分,它也不能勝任。

3、軟中斷和tasklet

在2.3這個開發版本中,核心開發者引入了軟中斷和tasklet。如果無須考慮和過去開發的驅動程式相容的話,軟中斷和tasklet可以完全代替BH介面。軟中斷是一組靜態定義的下半部介面,有32個,可以在所有處理器上同時執行——即使兩個型別相同也可以。tasklet是一種基於軟中斷實現的靈活性強、動態建立的下半部實現機制。兩個不同型別的tasklet可以在不同的處理器上同時執行,但型別相同的tasklet不能同時執行。tasklet其實是一種在效能和易用性之間尋求平衡的產物。對於大部分下半部處理來說,用tasklet就足夠了,像網路這樣對效能要求非常高的情況下才需要使用軟中斷。使用軟中斷需要特別小心,因為兩個相同的軟中斷有可能同時被執行。軟中斷還必須在編譯期間就進行靜態註冊。相反,tasklet可以通過程式碼進行動態註冊。

在2.6版本的核心中,核心提供了三種不同形式的下半部實現機制:軟中斷、tasklet和工作佇列。

核心定時器

另外一個可以用於將工作推後執行的機制是核心定時器。核心定時器把操作推遲到某個確定的時間段之後執行。但是當必須保證在一個確定的時間段過去以後再執行時,應該使用核心定時器。

4、混亂的下半部概念

當前,有三種機制可以用來實現將工作推後執行:軟中斷、tasklet和工作佇列。tasklet通過軟中斷實現,而工作佇列與它們完全不同。下半部機制的演化例程。