今天聊下移植RTOS時(shí)RISC-V內(nèi)核時(shí)單片機(jī)任務(wù)棧保存哪些內(nèi)容。
上一章中列舉了所有的寄存器,當(dāng)需要切換任務(wù)時(shí)刻的寄存器值,除x0恒為0,其他的寄存器無法預(yù)知其值,切換時(shí)均需要保存(gp寄存器編譯好后,固定不變,理論上可以不操作,為保持一致性和完整性,一并保存),如果使用浮點(diǎn),還應(yīng)該包括浮點(diǎn)寄存器。 每個(gè)RTOS均會(huì)定義一個(gè)和上下文保存相關(guān)的結(jié)構(gòu)體,以rt-thread為例,可以看到如下圖1的數(shù)據(jù)結(jié)構(gòu)定義。
圖1 上下文保存結(jié)構(gòu)體
可以看到除了通用寄存器外,還有兩個(gè)前文提到的成員mepc、mstatus,其中mstatus中含有中斷的使能控制位,而mepc為機(jī)器模式下異常程序指針寄存器,其值會(huì)在執(zhí)行mret后更新給pc,我們正式通過設(shè)置該寄存器的值來控制程序運(yùn)行的切換。
當(dāng)我們新建一個(gè)線程,初始化線程時(shí),會(huì)為其開辟一個(gè)線程棧(程序中通常設(shè)置一個(gè)數(shù)組),即對(duì)上述結(jié)構(gòu)體做初始化,在rt-thread中的代碼如下圖2所示。
圖2 線程堆棧初始化
由程序可知,堆棧初始化在線程初始化中被調(diào)用,線程初始化程序中首先將整個(gè)堆棧空間設(shè)成“#”,然后根據(jù)堆棧的增長(zhǎng)方向設(shè)置不同參數(shù),以紅框中的向下增長(zhǎng)為例,將線程的入口位置,線程可以帶一個(gè)參數(shù),返回地址,堆棧頂部地址。從堆棧初始化程序*rt_hw_stack_init中可以看出,其先將堆棧頂部地址對(duì)齊,然后向下偏移一個(gè)rt_hw_stack_frame結(jié)構(gòu)體的大小,用于存儲(chǔ)圖1中需要存儲(chǔ)的寄存器,并對(duì)該部分空間進(jìn)行了初始化。其中把線程的入口地址給了mepc,線程輸入?yún)?shù)給a0,mstatus初始值(MPP、MPIE、FS、MIE),即強(qiáng)制機(jī)器模式,使能浮點(diǎn),MPIE為1,MIE為0。如果不帶硬件浮點(diǎn),可將該值設(shè)置為0x1880。另外設(shè)置ra為線程的返回地址,一般情況下一個(gè)線程我們希望一直運(yùn)行的,當(dāng)需要返回時(shí)說明該線程不再需要運(yùn)行,所以返回地址一般是一段將該線程從線程列表中刪除并切換至下一個(gè)線程的一段程序,即圖2紅框的中調(diào)用的函數(shù)_rt_thread_exit。
初始化線程時(shí)會(huì)定義一個(gè)rt_thread結(jié)構(gòu)的全局變量,線程的操作即依靠該結(jié)構(gòu)體。其內(nèi)部?jī)?nèi)容如下圖3所示,其內(nèi)部可以看到一個(gè)sp成員,初始化好的堆棧指針即傳給該成員。
圖3 rt_thread結(jié)構(gòu)體詳情
綜上可以看出有每個(gè)線程一個(gè)rt_thread結(jié)構(gòu)體,由rt_thread->sp可獲得該線程的堆棧位置,堆棧的棧頂?shù)膕izeof(rt_hw_stack_frame)空間存放了該線程運(yùn)行需要的CPU寄存器值,剩余空間用于該線程運(yùn)行時(shí)變量的出入棧。
以上的內(nèi)容在其他RTOS中也能看到,例如上下文保存結(jié)構(gòu)體rt_hw_stack_frame在華為鴻蒙LiteOS_M中有TaskContext,TencentOS_Tiny中有cpu_context_t,而線程管理的結(jié)構(gòu)體rt_thread,LiteOS_M中LosTaskCB,TencentOS_Tiny中有k_task_st等。
-
單片機(jī)
+關(guān)注
關(guān)注
6032文章
44525瀏覽量
633256 -
寄存器
+關(guān)注
關(guān)注
31文章
5325瀏覽量
120052 -
內(nèi)核
+關(guān)注
關(guān)注
3文章
1366瀏覽量
40235 -
RTOS
+關(guān)注
關(guān)注
22文章
809瀏覽量
119451 -
RISC-V
+關(guān)注
關(guān)注
44文章
2233瀏覽量
46045
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論