溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何進(jìn)行rt-thread中的壓棧與出棧分析

發(fā)布時間:2021-12-17 14:47:05 來源:億速云 閱讀:279 作者:柒染 欄目:互聯(lián)網(wǎng)科技

如何進(jìn)行rt-thread中的壓棧與出棧分析,相信很多沒有經(jīng)驗的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。

rt-thread中的壓棧與出棧

1.說明

主要想分析一下rt-thread中線程的壓棧與入棧的相關(guān)操作。從而更好的掌握線程切換與線程恢復(fù)的相關(guān)知識。

 

2.使用場景

首先需要明白的是什么情況下需要進(jìn)行壓棧與出棧的操作?對于這個問題可以做這樣的設(shè)想,當(dāng)程序一直做一件事的時候,是順序執(zhí)行的,不會有任何干擾。但是此時來了一個中斷,那么程序邏輯肯定會優(yōu)先去處理中斷。那么這時需要做哪些事情?

也許這個例子有點脫離實際,講的通俗明白一些,就是一個人在專注的完成一件事,此時應(yīng)該是很順利的進(jìn)行。但是當(dāng)事情還沒有做完,但是又有一個更加緊急的事情需要你去處理,這時應(yīng)該怎么做?

對于一個人來講:

(1)將手里沒有做完的事情保留起來,保留當(dāng)前的進(jìn)度

(2)將大腦清空,全力完成更加重要的事情

(3)恢復(fù)到?jīng)]有做完的事情的現(xiàn)場,去接著完成沒有做完的事情

人的大腦是這樣工作的,其實芯片的邏輯也需要這樣執(zhí)行,我們知道芯片如何知道當(dāng)前程序的狀態(tài),無外乎幾個重要的寄存器,sp(程序指針寄存器),通用寄存器Rx,以及LR鏈接寄存器等等。有了這些信息,就可以知道程序當(dāng)前運行的狀態(tài)了,這個就是程序的現(xiàn)場。

對于armv7來說,寄存器可以分為以下幾種:

如何進(jìn)行rt-thread中的壓棧與出棧分析

armasm_pge1464343210583

在rt-thread操作系統(tǒng)中,涉及到壓棧與出棧操作的有兩個地方,第一個是中斷的進(jìn)入與中斷處理完成后的退出,第二個是線程的切換。

 

3.簡單分析一下rt-thread線程棧的初始化

對于/bsp/qemu-vexpress-a9來說,系統(tǒng)上電后執(zhí)行rtt的第一行代碼在/libcpu/arm/cortex-a/start_gcc.S文件。

然后執(zhí)行_reset函數(shù),這個函數(shù)是匯編函數(shù)寫的,因為前期沒有??臻g,所以代碼需要采用匯編指令完成。

然后分配??臻g等等。執(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)存就是在棧的入口處。

當(dāng)線程發(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)容:

當(dāng)線程間要從上一個線程切換到下一個線程的時候,首先會將切換之前現(xiàn)場保存起來,也就是將這些寄存器的知保存到內(nèi)存中,然后將sp指向下線程的地址。此時需要恢復(fù)下一個需要切換的線程的寄存器。

如果需要理清楚rt-thread的??臻g的壓棧與入棧,其實最根本的問題就是如何去處理現(xiàn)場狀態(tài)的問題。也就是每個線程都需要有一個獨立的??臻g,然后這些??臻g除了保存數(shù)據(jù),還需要保存寄存器。當(dāng)進(jìn)行任務(wù)切換的時候,當(dāng)前線程的寄存器需要保存該線程的棧內(nèi)存中,而下個線程的??臻g則會從自己的棧空間的起始地址處恢復(fù)。這個就是rt-thread棧運作的實現(xiàn)邏輯。

看完上述內(nèi)容,你們掌握如何進(jìn)行rt-thread中的壓棧與出棧分析的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI