標籤:http io ar os 使用 sp on 2014 問題
核心空間和使用者空間是現代作業系統的兩種工作模式,核心模組運行在核心空間,而 使用者態應用程式運行在使用者空間。它們代表不同的層級,而對系統資源具有不同的存取權限。核心模組運行在最進階別(核心態),這個級下所有的操作都受系統信 任,而應用程式運行在較低層級(使用者態)。在這個層級,處理器控制著對硬體的直接存取以及對記憶體的非授權訪問。核心態和使用者態有自己的記憶體映射,即自己的 地址空間。
系統的兩種不同於行狀態,才有了內容相關的概念。使用者空間的應用程式,如果想請求系統服務,比如操作某個物理裝置,映射裝置的地址到使用者空間,必須通過系統調用來實現。(系統調用是作業系統提供給使用者空間的介面函數)。如所示:
通過系統調用,使用者空間的應用程式就會進入核心空間,由核心代表該進程運行於核心空間,這就涉及到內容相關的切換,使用者空間和核心空間具有不同的 地址映射,通用或專用的寄存器組,而使用者空間的進程要傳遞很多變數、參數給核心,核心也要儲存使用者進程的一些寄存器、變數等,以便系統調用結束後回到使用者 空間繼續執行,所謂的進程上下文,就是一個進程在執行的時候,CPU的所有寄存器中的值、進程的狀態以及堆棧上的內容,當核心需要切換到另一個進程時,它 需要儲存當前進程的所有狀態,即儲存當前進程的進程上下文,以便再次執行該進程時,能夠恢複切換時的狀態,繼續執行。
同理,硬體通過觸發訊號,向CPU發送中斷訊號,導致核心調用中斷處理常式,進入核心空間。這個過程中,硬體的一些變數和參數也要傳遞給核心, 核心通過這些參數進行中斷處理,中斷上下文就可以理解為硬體傳遞過來的這些參數和核心需要儲存的一些環境,主要是被中斷的進程的環境。
Linux核心工作在進程上下文或者中斷上下文。提供系統調用服務的核心代碼代表發起系統調用的應用程式運行在進程上下文;另一方面,中斷處理常式,非同步運行在中斷上下文。中斷上下文和特定進程無關。
運行在進程內容相關的核心代碼是可以被搶佔的(Linux2.6支援搶佔)。但是一個中斷上下文,通常都會始終佔有CPU(當然中斷可以嵌套,但我們一般不這樣做),不可以被打斷。正因為如此,運行在中斷內容相關的代碼就要受一些限制,不能做下面的事情:
1. 睡眠或者放棄CPU。
由於中斷上下文不屬於任何進程,它與current沒有任何關係(儘管此時current指向被中斷的進程),所以中斷上下文一旦睡眠或者放棄CPU,將無法被喚醒。所以也叫原子上下文(atomic context)。
2. 嘗試獲得訊號量
為了保護中斷控制代碼臨界區資源,不能使用mutexes。如果獲得不到訊號量,代碼就會睡眠,會產生和上面相同的情況,如果必須使用鎖,則使用spinlock。
3. 執行耗時的任務
中斷處理應該儘可能快,因為核心要響應大量服務和請求,中斷上下文佔用CPU時間太長會嚴重影響系統功能。在中斷處理常式中執行耗時任務時,應該交由中斷處理常式底半部來處理。
4. 訪問使用者空間的虛擬位址
因為中斷上下文是和特定進程無關的,它是核心代表硬體運行在核心空間,所以在終端上下文無法訪問使用者空間的虛擬位址
5. 中斷處理常式不應該設定成reentrant(可被並行或遞迴調用的常式)。因為中斷髮生時,preempt和irq都被disable,直到中斷返回。所以中斷上下文和進程上下文不一樣,中斷處理常式的不同執行個體,是不允許在SMP上並發啟動並執行。
6. 中斷處理常式可以被更進階別的IRQ中斷。如果想禁止這種中斷,可以將中斷處理常式定義成快速處理常式,相當於告訴CPU,該常式運行時,禁止本地CPU上所有插斷要求。這直接導致的結果是,由於其他中斷被延遲響應,系統效能下降。
核心的一個基本原則就是:在中斷或者說原子上下文中,核心不能訪問使用者空間,而且核心是不能 睡眠的。也就是說在這種情況下,核心是不能調用有可能引起睡眠的任何函數。一般來講原子上下文指的是在中斷或非強制中斷中,以及在持有自旋鎖的時候。核心提供 了四個宏來判斷是否處於這幾種情況裡:
#define in_irq() (hardirq_count()) //在處理硬中斷中
#define in_softirq() (softirq_count()) //在處理非強制中斷中
#define in_interrupt() (irq_count()) //在處理硬中斷或非強制中斷中
#define in_atomic() ((preempt_count() & ~PREEMPT_ACTIVE) != 0) //包含以上所有情況
這四個宏所訪問的count都是thread_info->preempt_count。這個變數其實是一個位元遮罩。最低8位表示搶佔計數,通常由spin_lock/spin_unlock修改,或程式員強制修改,同時表明核心容許的最大搶佔深度是256。
8-15位表示非強制中斷計數,通常由local_bh_disable/local_bh_enable修改,同時表明核心容許的最大非強制中斷深度是256。
位16-27是硬中斷計數,通常由enter_irq/exit_irq修改,同時表明核心容許的最大硬中斷深度是4096。
第28位是PREEMPT_ACTIVE標誌。用代碼錶示就是:
PREEMPT_MASK: 0x000000ff
SOFTIRQ_MASK: 0x0000ff00
HARDIRQ_MASK: 0x0fff0000
凡是上面4個宏返回1得到地方都是原子上下文,是不容許核心訪問使用者空間,不容許核心睡眠的,不容許調用任何可能引起睡眠的函數。而且代表thread_info->preempt_count不是0,這就告訴核心,在這裡面搶佔被禁用。
但 是,對於in_atomic()來說,在啟用搶佔的情況下,它工作的很好,可以告訴核心目前是否持有自旋鎖,是否禁用搶佔等。但是,在沒有啟用搶佔的情況 下,spin_lock根本不修改preempt_count,所以即使核心調用了spin_lock,持有了自旋鎖,in_atomic()仍然會返回 0,錯誤的告訴核心目前在非原子上下文中。所以凡是依賴in_atomic()來判斷是否在原子內容相關的代碼,在禁搶佔的情況下都是有問題的。
【轉】進程上下文和中斷上下文、原子內容相關的區別