1、Cortex-M3 的PSP和MSP
曾經在STM32上使用過RT thread和uC/OS,對於任務切換代碼一直是一知半解,沒有自己手動寫出來過,對於任務切換后的ORR LR, LR, #0x04; Ensure exception return uses process stack也不是很理解,一次偶然的機會,遇到網上有人問這個問題,才去深入研究一下。Cortex‐M3 擁有兩個堆棧指針,然而它們是banked,因此任一時刻只能使用其中的一個:
主堆棧指針(MSP):復位后缺省使用的堆棧指針,用於操作系統內核以及異常處理例程(包括中斷服務例程)。
進程堆棧指針(PSP):由用戶的應用程序代碼使用。堆棧指針的最低兩位永遠是0,這意味着堆棧總是4 字節對齊的。
由此可以看出,在一個系統里有兩大部分,一是操作系統和中斷,一是用戶用戶程序,他們使用的資源是不一樣的,在任務調度的時候,中斷(任務調度的中斷)返回到用戶程序所以要使用PSP。
2、Linux系統中斷中能否使用Sleep
這個是我一個同學的面試題,說給我的時候我第一反應是可以,在中斷中使用Sleep不會使得中斷掛起,在任務中使用Sleep會將當前的任務掛起,Sleep的原理是根據Sleep的參數N暫停當前的任務,N個周期(也可能是MS)后將該任務置為就緒態,換言之,Sleep實際上就是一次任務調度,那為什么不可以的,我還進一步解釋,在uC/OS中,在中斷的結尾都會調用OS_INT_EXIT()函數來進行任務的調度,Sleep與OS_INT_EXIT只是函數名不同,最終都是會調用調度器的。
哈哈,對於正確答案我想大家都猜到了,那就是當然不可以的啦,但是我的解釋貌似天衣無縫哦,那是因為我是使用uC/OS來解釋的,Linux系統當然和uC/OS不同,回頭看看,在Cortex-M3的硬件中有MSP和PSP,那么他的設計初衷是什么,那就是操作系統(包括中斷)和用戶程序分(隔)離,或者說是保護操作系統,防止遭到破壞,在這種思想下,Linux系統中,對於涉及到系統核心的東西使用保護的,不是所有的地方都是可見的,uC/OS是系統和應用程序一起編譯,只要是全局變量都是可以見的,而Linux不同,在中斷上下為中,current宏是無效的,所以此時是無法進行任務調度的。
以上是我結合Cortex-M3內核的硬件,從隔離和保護的思想去解釋為什么Linux下,中斷中不能使用Sleep。下面給出我查閱資料的官方解釋,主要是《linux內核設計與實現》上的內容。
1、進程上下文
可執行代碼是進程的重要組成部分,這些代碼從一個可執行文件載入到進程的地址空間執行。一般的程序在用戶空間執行,當一個程序執行了系統調用或者觸發了某個異常,它就陷入了內核空間,此時,我們稱內核代表進程執行並處於進程上下文中。在此上下文中current宏是有效的。(系統調用和異常處理程序是對內核明確定義的接口,進程只有通過這些接口才能陷入內核執行—對內核的所有訪問都必須通過這些接口。
2、中斷上下文
當執行一個中斷處理程序時,內核處於中斷上下文中,中斷上下文和進程並沒有任何瓜葛。與current宏也是不相干的(盡管它會指向被被中斷的進程)。因為沒有后備進程,所以中斷上下文不可以睡眠,否則又怎能再對它重新調度呢?因此,不能從中斷上下文中調用某些函數(就是被中斷處理程序限制的函數,Sleep就是其中一個)。
事實上,除了MSP和PSP之外,處理器很多的寄存器都是banked,用戶程序和中斷程序使用的寄存器是不同的,所以進程上下文和中斷上下文環境是不同的,如果在中斷上下文中進行任務調度,是沒有辦法保存進程上下文信息的,所以如果這個時候調度就是有問題的,軟件上阻止了該調度——即current宏此時是不可見的,從而無法進行調度。