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