如何進行rt-thread中的壓棧與出棧分析,相信很多沒有經(jīng)驗的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。
成都創(chuàng)新互聯(lián)公司一直秉承“誠信做人,踏實做事”的原則,不欺瞞客戶,是我們最起碼的底線! 以服務為基礎,以質(zhì)量求生存,以技術求發(fā)展,成交一個客戶多一個朋友!為您提供成都做網(wǎng)站、網(wǎng)站制作、成都網(wǎng)頁設計、成都小程序開發(fā)、成都網(wǎng)站開發(fā)、成都網(wǎng)站制作、成都軟件開發(fā)、成都App定制開發(fā)是成都本地專業(yè)的網(wǎng)站建設和網(wǎng)站設計公司,等你一起來見證!
主要想分析一下rt-thread中線程的壓棧與入棧的相關操作。從而更好的掌握線程切換與線程恢復的相關知識。
首先需要明白的是什么情況下需要進行壓棧與出棧的操作?對于這個問題可以做這樣的設想,當程序一直做一件事的時候,是順序執(zhí)行的,不會有任何干擾。但是此時來了一個中斷,那么程序邏輯肯定會優(yōu)先去處理中斷。那么這時需要做哪些事情?
也許這個例子有點脫離實際,講的通俗明白一些,就是一個人在專注的完成一件事,此時應該是很順利的進行。但是當事情還沒有做完,但是又有一個更加緊急的事情需要你去處理,這時應該怎么做?
對于一個人來講:
(1)將手里沒有做完的事情保留起來,保留當前的進度
(2)將大腦清空,全力完成更加重要的事情
(3)恢復到?jīng)]有做完的事情的現(xiàn)場,去接著完成沒有做完的事情
人的大腦是這樣工作的,其實芯片的邏輯也需要這樣執(zhí)行,我們知道芯片如何知道當前程序的狀態(tài),無外乎幾個重要的寄存器,sp(程序指針寄存器),通用寄存器Rx,以及LR鏈接寄存器等等。有了這些信息,就可以知道程序當前運行的狀態(tài)了,這個就是程序的現(xiàn)場。
對于armv7來說,寄存器可以分為以下幾種:
在rt-thread操作系統(tǒng)中,涉及到壓棧與出棧操作的有兩個地方,第一個是中斷的進入與中斷處理完成后的退出,第二個是線程的切換。
對于/bsp/qemu-vexpress-a9來說,系統(tǒng)上電后執(zhí)行rtt的第一行代碼在/libcpu/arm/cortex-a/start_gcc.S文件。
然后執(zhí)行_reset函數(shù),這個函數(shù)是匯編函數(shù)寫的,因為前期沒有棧空間,所以代碼需要采用匯編指令完成。
然后分配棧空間等等。執(zhí)行到rtt的其他部分邏輯。這里就不贅述了。這里主要分析的是線程的初始化。
每一個線程在初始化的時候,需要分配??臻g
rt_thread_create/rt_thread_init --> _rt_thread_init --> rt_hw_stack_init
最后調(diào)用到了/libcpu/arm/cortex-a/stack.c文件。
rt_uint8_t *rt_hw_stack_init(void *tentry, void *parameter,
rt_uint8_t *stack_addr, void *texit)
{
rt_uint32_t *stk;
stack_addr += sizeof(rt_uint32_t);
stack_addr = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stack_addr, 8);
stk = (rt_uint32_t *)stack_addr;
*(--stk) = (rt_uint32_t)tentry; /* entry point */
*(--stk) = (rt_uint32_t)texit; /* lr */
*(--stk) = 0xdeadbeef; /* r12 */
*(--stk) = 0xdeadbeef; /* r11 */
*(--stk) = 0xdeadbeef; /* r10 */
*(--stk) = 0xdeadbeef; /* r9 */
*(--stk) = 0xdeadbeef; /* r8 */
*(--stk) = 0xdeadbeef; /* r7 */
*(--stk) = 0xdeadbeef; /* r6 */
*(--stk) = 0xdeadbeef; /* r5 */
*(--stk) = 0xdeadbeef; /* r4 */
*(--stk) = 0xdeadbeef; /* r3 */
*(--stk) = 0xdeadbeef; /* r2 */
*(--stk) = 0xdeadbeef; /* r1 */
*(--stk) = (rt_uint32_t)parameter; /* r0 : argument */
/* cpsr */
if ((rt_uint32_t)tentry & 0x01)
*(--stk) = SVCMODE | 0x20; /* thumb mode */
else
*(--stk) = SVCMODE; /* arm mode */
#ifdef RT_USING_LWP
*(--stk) = 0; /* user lr */
*(--stk) = 0; /* user sp*/
#endif
#ifdef RT_USING_FPU
*(--stk) = 0; /* not use fpu*/
#endif
/* return task's current stack address */
return (rt_uint8_t *)stk;
}
初始化線程的時候,每個線程都是有一個??臻g的,這個??臻g不僅僅保存一下參數(shù)變量,還在棧地址的首地址處保存了線程執(zhí)行需要的現(xiàn)場。而且每個線程都有一個獨立的棧內(nèi)存,這個內(nèi)存就是在棧的入口處。
當線程發(fā)生切換的時候,需要取出這些寄存器
.globl rt_thread_switch_interrupt_flag
.globl rt_interrupt_from_thread
.globl rt_interrupt_to_thread
.globl rt_hw_context_switch_interrupt
rt_hw_context_switch_interrupt:
#ifdef RT_USING_SMP
/* r0 :svc_mod context
* r1 :addr of from_thread's sp
* r2 :addr of to_thread's sp
* r3 :to_thread's tcb
*/
str r0, [r1]
ldr sp, [r2]
mov r0, r3
bl rt_cpus_lock_status_restore
b rt_hw_context_switch_exit
執(zhí)行到rt_hw_context_switch_exit函數(shù)
.global rt_hw_context_switch_exit
rt_hw_context_switch_exit:
#ifdef RT_USING_SMP
#ifdef RT_USING_SIGNALS
mov r0, sp
cps #Mode_IRQ
bl rt_signal_check
cps #Mode_SVC
mov sp, r0
#endif
#endif
#ifdef RT_USING_FPU
/* fpu context */
ldmfd sp!, {r6}
vmsr fpexc, r6
tst r6, #(1<<30)
beq 1f
ldmfd sp!, {r5}
vmsr fpscr, r5
vldmia sp!, {d16-d31}
vldmia sp!, {d0-d15}
1:
#endif
#ifdef RT_USING_LWP
ldmfd sp, {r13, r14}^ /* usr_sp, usr_lr */
add sp, #8
#endif
ldmfd sp!, {r1}
msr spsr_cxsf, r1 /* original mode */
ldmfd sp!, {r0-r12,lr,pc}^ /* irq return */
該函數(shù)可能看起來有些費勁,我來解釋一下大概的內(nèi)容:
當線程間要從上一個線程切換到下一個線程的時候,首先會將切換之前現(xiàn)場保存起來,也就是將這些寄存器的知保存到內(nèi)存中,然后將sp指向下線程的地址。此時需要恢復下一個需要切換的線程的寄存器。
如果需要理清楚rt-thread的??臻g的壓棧與入棧,其實最根本的問題就是如何去處理現(xiàn)場狀態(tài)的問題。也就是每個線程都需要有一個獨立的??臻g,然后這些??臻g除了保存數(shù)據(jù),還需要保存寄存器。當進行任務切換的時候,當前線程的寄存器需要保存該線程的棧內(nèi)存中,而下個線程的??臻g則會從自己的??臻g的起始地址處恢復。這個就是rt-thread棧運作的實現(xiàn)邏輯。
看完上述內(nèi)容,你們掌握如何進行rt-thread中的壓棧與出棧分析的方法了嗎?如果還想學到更多技能或想了解更多相關內(nèi)容,歡迎關注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!