本文簡單討論RT-Thread在啟動后,逐步進入到處于就緒態最高優先級main線程的全過程。部分內容涉及到匯編指令,但通俗易懂。通過簡化工程,配合Debug過程,逐步觀察寄存器的變化、繪制棧幀結構、繪制線程控制塊和rt_interrupt_from_thread、rt_interrupt_to_thread等典型變量取值(指向,雖然是rt_uint32_t類型,但實際在匯編中是作為指針使用),能有效幫助理解RTOS的線程棧的恢復與啟動過程。
通過本文對線程啟動過程的了解,對于兩個線程/多個線程之間的互相切換能奠定堅實的基礎,化繁為簡,結合論壇關于上下文切換的代碼注釋,能幫助快速抓住主線。
使用的軟硬件環境如下:
IDE工具 - RT-Thread Studio 2.2.6
硬件 - STM32L431RCT6,Cortex M4內核
軟件 - RT-Thread 4.0.5版本
配置 - 僅使能main線程和tidle0線程
一、工程設置
Step 1. 新建名稱為EVBMX_RTThread405_Switch的4.0.5版本工程
Step 2. 不使能軟件定時器,使能線程狀態更改的調試
關閉軟件定時器線程,避免干擾。
Step 3. 關閉msh shell,禁用Finsh
關閉tshell線程,避免干擾。僅僅保留main線程和tidle0線程。
Step 4. 修改main函數
修改main函數后,線程進入一次,休眠且切換1次,再次切回且return,然后徹底退出,只留下tidle0線程。
#include
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include
int main(void)
{
rt_thread_mdelay(1000);
return RT_EOK;
}
Step 5. 下載程序,觀察輸出結果
讀完全文后,對下方輸出結果的每一行語句所代表的含義和發生時刻,能有更深刻體會。
二、調試運行
Step 6. 在component.c中257行按F9設置斷點;F5全速運行到此處后,再按F9關閉此處斷點。
Step 7. 依次進入rt_thread_create, _thread_init, 停留在thread.c的164行。
將變量thread添加到表達式窗口,可以查看各個成員的值,其中,thread->stack_addr = 0x20001138, thread->stack_size = 0x800,分別表示棧底位置和棧空間大小。
164行的函數rt_hw_stack_init對于理解線程切換是一個相當重要的函數,其形參分別為:
線程入口函數:main_thread_entry
線程參數RT_NULL:
線程棧棧頂地址:thread->stack_addr + thread->stack_size - 4 = 0x20001138 + 0x800 - 4 = 0x20001934
Step 8. 單步進入到rt_hw_stack_init函數內部,開展分析
149行,由于傳遞進來的stack_addr = 0x20001934,執行完畢后,stk為0x20001938。從0x20001138(含)到0x20001934(含),合計是0x800 = 2048字節。STM32使用的滿遞減棧,所以此處的stk是0x20001938。
150行,此處設置8字節對齊。由于0x20001938 = (536877368)Decimal,該數據除8等于67109671,能被8整除,該語句執行棧對齊操作后,stk依然為0x20001938。
Step 9. 繼續了解rt_hw_stack_init函數。
151行,更新stk的值,減去struct stack_frame結構體的大小。執行完畢后,stk = 0x200018F4。
153行,stack_frame指針指向0x200018F4。
156至159行,通過for循環將0x200018F4至0x20001938的所有內存變成0xdeadbeaf魔法字。
161行至168行,將stack_frame成員的exception_stack_frame中的r0~psr共8個寄存器分別設置為:線程參數,4個0,線程返回地址,線程入口地址,0x01000000。
175行,返回stk的值,此時變成0x200018F4。這個值在初始化線程時,將返回給thread->sp,即線程棧的臨時棧頂指針。
依次將線程的形參、r1-r3, r12, 線程返回地址、線程入口地址,線程的xPSR寫入異常棧幀結構中。
在初入門時,這里是難點。C語言中使用結構體定義的棧結構,如何和實際寄存器的順序進行一一對應?,后文會通過逐步Debug揭示這個問題答案。
至此,main線程創建完畢后,線程結構體和線程棧空間如下所示。
Step 10. 繼續單步到rt_system_scheduler_start函數處,并單獨跟蹤進入到該函數內部。
期間,RT-Thread會調用rt_thread_idle_init函數,在該函數中使用靜態創建方式初始化tidle0線程。可以按照上述過程記錄tidle0線程的棧空間。
-
寄存器
+關注
關注
31文章
5357瀏覽量
120693 -
STM32
+關注
關注
2270文章
10910瀏覽量
356615 -
C語言
+關注
關注
180文章
7608瀏覽量
137154 -
Shell
+關注
關注
1文章
366瀏覽量
23414 -
RT-Thread
+關注
關注
31文章
1296瀏覽量
40242
發布評論請先 登錄
相關推薦
評論