Q:
RT-Thread 在線程初始化的代碼內(nèi)有一段初始化線程堆棧的代碼,如下:
thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter,
(rt_uint8_t *)((char *)thread->stack_addr + thread->stack_size - sizeof(rt_ubase_t)),
(void *)_thread_exit);
在調(diào)用 rt_hw_stack_init() 初始化堆棧的時(shí)候傳入線程棧起始地址進(jìn)行了 -sizeof(rt-ubase_t) 操作,而在 rt_hw_stack_init() 函數(shù)內(nèi)又進(jìn)行 stk = stack_addr + sizeof(rt_uint32_t); 將其給加了回去,這操作的意義是什么呢?還是說(shuō)是歷史遺留問(wèn)題?
在《野火 RT-Thread內(nèi)核實(shí)現(xiàn)與應(yīng)用開(kāi)發(fā)指南》內(nèi)也有說(shuō)到此處的設(shè)計(jì),但并未進(jìn)行升入說(shuō)明,僅簡(jiǎn)單的一筆帶過(guò),因此大多數(shù)讀者和我一樣都對(duì)此充滿疑問(wèn)。
A:
1.rt_hw_stack_init調(diào)用分析
分析此問(wèn)題,首先我們需要結(jié)合完整版本的 rt-thread 內(nèi)核代碼進(jìn)行閱讀才能更好的充分理解。
在rt-thread內(nèi)核代碼中,初始化線程堆棧的時(shí)候其實(shí)是有一個(gè)宏聲明進(jìn)行選擇的,具體代碼如下:
#ifdef ARCH_CPU_STACK_GROWS_UPWARD
thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter,
(void *)((char *)thread->stack_addr),
(void *)_thread_exit);
#else
thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter,
(rt_uint8_t *)((char *)thread->stack_addr + thread->stack_size - sizeof(rt_ubase_t)),
(void )_thread_exit);
#endif / ARCH_CPU_STACK_GROWS_UPWARD */
也就是針對(duì)不同架構(gòu)的CPU實(shí)際傳入此函數(shù)的參數(shù)還存在著不一樣的地方!
針對(duì) ==棧是向下增長(zhǎng)型== 的CPU架構(gòu),傳入的參數(shù)為:(rt_uint8_t *)((char *)thread->stack_addr + thread->stack_size - sizeof(rt_ubase_t))
針對(duì) ==棧是向上增長(zhǎng)型== 的CPU架構(gòu),傳入的參數(shù)為:(void *)((char *)thread->stack_addr)
而此參數(shù)的含義為棧的起始地址!
線程的棧也就是一塊連續(xù)地址空間的數(shù)組,這個(gè)是理解棧的前提;針對(duì)向上增長(zhǎng)型的棧,棧起始地址就是 thread->stack_addr 這很好理解,對(duì)于向下增長(zhǎng)型的棧,就需要注意了,起始地址并不是,thread->stack_addr + thread->stack_size?。?!
既然棧就是一塊數(shù)組,那我們不妨用數(shù)組來(lái)理解,char table[100],數(shù)組table的最頂部的成員不是table[100],而是table[99],即table[100-1]。因此向下增長(zhǎng)的棧從頂部往底部填充數(shù)據(jù)就類似于數(shù)組從尾部往頭部填充數(shù)據(jù),起始地址為: (rt_uint8_t *)((char *)thread->stack_addr + thread->stack_size - sizeof(rt_ubase_t))
同時(shí)此處的代碼是放在 thread.c 內(nèi),thread.c是內(nèi)核文件,是公共的文件,不管你是什么硬件平臺(tái),不管你是什么CPU架構(gòu),在內(nèi)核的角度看,它只管給 rt_hw_stack_init 函數(shù)傳入棧的起始地址即可,因此針對(duì)向下增長(zhǎng)型的棧在這里 -sizeof(rt_ubase_t)) 并沒(méi)有任何問(wèn)題。
再往下層,具體到cpu上,每個(gè)cpu都會(huì)有對(duì)應(yīng)的 cpuport.c 來(lái)實(shí)現(xiàn)對(duì)應(yīng)的 rt_hw_stack_init 函數(shù),并根據(jù)各自的cpu結(jié)構(gòu)來(lái)實(shí)現(xiàn)具體的線程棧初始化。
2.rt_hw_stack_init 實(shí)現(xiàn)分析
2.1 向下增長(zhǎng)型棧 rt_hw_stack_init 實(shí)現(xiàn)
針對(duì)向下增長(zhǎng)型的棧,以 cortex-m4 內(nèi)核為例:
rt_uint8_t *rt_hw_stack_init(void *tentry,
void *parameter,
rt_uint8_t *stack_addr,
void *texit)
{
struct stack_frame *stack_frame;
rt_uint8_t *stk;
unsigned long i;
stk = stack_addr + sizeof(rt_uint32_t);
stk = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stk, 8);
stk -= sizeof(struct stack_frame);
stack_frame = (struct stack_frame )stk;
/ init all register */
for (i = 0; i < sizeof(struct stack_frame) / sizeof(rt_uint32_t); i ++)
{
((rt_uint32_t )stack_frame)[i] = 0xdeadbeef;
}
stack_frame->exception_stack_frame.r0 = (unsigned long)parameter; / r0 : argument /
stack_frame->exception_stack_frame.r1 = 0; / r1 /
stack_frame->exception_stack_frame.r2 = 0; / r2 /
stack_frame->exception_stack_frame.r3 = 0; / r3 /
stack_frame->exception_stack_frame.r12 = 0; / r12 /
stack_frame->exception_stack_frame.lr = (unsigned long)texit; / lr /
stack_frame->exception_stack_frame.pc = (unsigned long)tentry; / entry point, pc /
stack_frame->exception_stack_frame.psr = 0x01000000L; / PSR /
#if USE_FPU
stack_frame->flag = 0;
#endif / USE_FPU /
/ return task's current stack address */
return stk;
}
繼續(xù)以char table[100]作為棧舉例:
stk = stack_addr + sizeof(rt_uint32_t); 拿到棧的最頂端的值,也就是100,注意table[100]這個(gè)成員是不能寫值的。
stk = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stk, 8); 之后按8字節(jié) 向下對(duì)齊,那就 stk 就變成了 96,table[97]、table[98]、table[99]由于字節(jié)對(duì)齊就保留了,后續(xù)也不會(huì)去使用,至于table[96]用沒(méi)用還不知道,我們接著看。
stk -= sizeof(struct stack_frame);,stk 減掉 struct stack_frame 結(jié)構(gòu)大小存儲(chǔ) struct stack_frame 結(jié)構(gòu)數(shù)據(jù),假定 struct stack_frame 大小4字節(jié), stk -= sizeof(struct stack_frame); 之后 stk 為92,之后寫4字節(jié)數(shù)據(jù),那么stk[92]、stk[93]、stk[94]、stk[95]填充了數(shù)據(jù),stk[96]不會(huì)去訪問(wèn)。
因此無(wú)論字節(jié)對(duì)齊的時(shí)候有沒(méi)有保留字節(jié),第一步stk雖然切換到了棧最頂端,但是并不會(huì)訪問(wèn)最頂端的那個(gè)成員,所以是安全的!
2.1 向上增長(zhǎng)型棧 rt_hw_stack_init 實(shí)現(xiàn)
注意向上增長(zhǎng)型棧初始化代碼就不是上面那一份了!上面我們說(shuō)了針對(duì)不同的cpu,會(huì)有不同的cpuport.c文件來(lái)實(shí)現(xiàn)對(duì)應(yīng)的 rt_hw_stack_init,因此我們需要找到向上增長(zhǎng)型的cpu對(duì)應(yīng)的cpuport.c來(lái)分析才行。
在rtthread內(nèi)核中,目前僅TI的tms320f28379為向上增長(zhǎng)型,對(duì)應(yīng)的cpuport.c在 libcpu/ti-dsp/c28x/cpuport.c內(nèi),它實(shí)現(xiàn)的 rt_hw_stack_init 函數(shù)如下:
(不要問(wèn)我怎么找到的,根據(jù)宏全局搜ARCH_CPU_STACK_GROWS_UPWARD=y能發(fā)現(xiàn)只有ti這顆用的向上增長(zhǎng)型! T_T)
rt_uint8_t *rt_hw_stack_init(void *tentry,
void *parameter,
rt_uint8_t *stack_addr,
void *texit)
{
struct stack_frame *stack_frame;
rt_uint8_t *stk;
unsigned long i;
stk = stack_addr;
stk = (rt_uint8_t )RT_ALIGN((rt_uint32_t)stk, 2);
stk += 1; / to work around the stack alignment /
stack_frame = (struct stack_frame )stk;
/ zero all registers /
for (i = 0; i < sizeof(struct stack_frame) / sizeof(rt_uint32_t); i ++)
{
((rt_uint32_t )stack_frame)[i] = 0;
}
/ configure special registers /
stack_frame->exception_stack_frame.dp_st1 = 0x00000A08;
stack_frame->xar4 = (rt_uint32_t)parameter;
stack_frame->exception_stack_frame.return_address = (rt_uint32_t)tentry;
stack_frame->rpc = (rt_uint32_t)texit;
#ifdef TMS320C28XX_FPU32
stack_frame->stf = 0x00000200;
stack_frame->rb = 0;
#endif
/ return task's current stack address */
return stk + sizeof(struct stack_frame);
}
向上增長(zhǎng)型就簡(jiǎn)單了,直接加就可以了,不過(guò)向上增長(zhǎng)型字節(jié)對(duì)齊采用的是 RT_ALIGN 向上對(duì)齊的方式!
擴(kuò)展知識(shí):
此外,關(guān)于棧除了向上增長(zhǎng)和向下增長(zhǎng)之外,還有一個(gè)知識(shí)點(diǎn):滿堆棧 和 空堆棧
概念介紹:
滿堆棧不是指堆棧滿了的意思,空堆棧也不是指堆??盏囊馑?,而是根據(jù)堆棧指針(SP指針)指向的空間是否存有數(shù)據(jù)來(lái)決定。
當(dāng)SP指針指向的地址空間沒(méi)有存放有效數(shù)據(jù),則稱之為空堆棧;
當(dāng)SP指針指向的地址空間存放有有效數(shù)據(jù),則稱之為滿堆棧。
因此針對(duì)滿堆棧,寫入數(shù)據(jù)的流程為先移動(dòng)SP指針再填寫有效數(shù)據(jù);而對(duì)于空堆棧則是先填寫有效數(shù)據(jù)再移動(dòng)堆棧指針。
由滿堆棧、空堆棧與向上增長(zhǎng)堆棧、向下增長(zhǎng)堆棧,共可組成四種組合:
向上遞增滿堆棧(滿增)
向下遞增滿堆棧(滿減)
向上遞增空堆棧(空增)
向下遞增空堆棧(空簡(jiǎn))
-
存儲(chǔ)器
+關(guān)注
關(guān)注
38文章
7492瀏覽量
163833 -
cpu芯片
+關(guān)注
關(guān)注
0文章
46瀏覽量
13614 -
Cortex-M4
+關(guān)注
關(guān)注
6文章
89瀏覽量
46551 -
FPU
+關(guān)注
關(guān)注
0文章
42瀏覽量
21316 -
RT-Thread
+關(guān)注
關(guān)注
31文章
1289瀏覽量
40129
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論