99精品伊人亚洲|最近国产中文炮友|九草在线视频支援|AV网站大全最新|美女黄片免费观看|国产精品资源视频|精彩无码视频一区|91大神在线后入|伊人终合在线播放|久草综合久久中文

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

rt-thread線程棧初始化參數(shù)分析

冬至子 ? 來源:jaffer ? 作者:jaffer ? 2023-08-14 16:50 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

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() 初始化堆棧的時候傳入線程棧起始地址進(jìn)行了 -sizeof(rt-ubase_t) 操作,而在 rt_hw_stack_init() 函數(shù)內(nèi)又進(jìn)行 stk = stack_addr + sizeof(rt_uint32_t); 將其給加了回去,這操作的意義是什么呢?還是說是歷史遺留問題?

在《野火 RT-Thread內(nèi)核實(shí)現(xiàn)與應(yīng)用開發(fā)指南》內(nèi)也有說到此處的設(shè)計,但并未進(jìn)行升入說明,僅簡單的一筆帶過,因此大多數(shù)讀者和我一樣都對此充滿疑問。

A:

1.rt_hw_stack_init調(diào)用分析

分析此問題,首先我們需要結(jié)合完整版本的 rt-thread 內(nèi)核代碼進(jìn)行閱讀才能更好的充分理解。

在rt-thread內(nèi)核代碼中,初始化線程堆棧的時候其實(shí)是有一個宏聲明進(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 */

1.jpg

也就是針對不同架構(gòu)的CPU實(shí)際傳入此函數(shù)的參數(shù)還存在著不一樣的地方!

針對 ==棧是向下增長型== 的CPU架構(gòu),傳入的參數(shù)為:(rt_uint8_t *)((char *)thread->stack_addr + thread->stack_size - sizeof(rt_ubase_t))
針對 ==棧是向上增長型== 的CPU架構(gòu),傳入的參數(shù)為:(void *)((char *)thread->stack_addr)
而此參數(shù)的含義為棧的起始地址!

線程的棧也就是一塊連續(xù)地址空間的數(shù)組,這個是理解棧的前提;針對向上增長型的棧,棧起始地址就是 thread->stack_addr 這很好理解,對于向下增長型的棧,就需要注意了,起始地址并不是,thread->stack_addr + thread->stack_size?。?!

既然棧就是一塊數(shù)組,那我們不妨用數(shù)組來理解,char table[100],數(shù)組table的最頂部的成員不是table[100],而是table[99],即table[100-1]。因此向下增長的棧從頂部往底部填充數(shù)據(jù)就類似于數(shù)組從尾部往頭部填充數(shù)據(jù),起始地址為: (rt_uint8_t *)((char *)thread->stack_addr + thread->stack_size - sizeof(rt_ubase_t))

同時此處的代碼是放在 thread.c 內(nèi),thread.c是內(nèi)核文件,是公共的文件,不管你是什么硬件平臺,不管你是什么CPU架構(gòu),在內(nèi)核的角度看,它只管給 rt_hw_stack_init 函數(shù)傳入棧的起始地址即可,因此針對向下增長型的棧在這里 -sizeof(rt_ubase_t)) 并沒有任何問題。

再往下層,具體到cpu上,每個cpu都會有對應(yīng)的 cpuport.c 來實(shí)現(xiàn)對應(yīng)的 rt_hw_stack_init 函數(shù),并根據(jù)各自的cpu結(jié)構(gòu)來實(shí)現(xiàn)具體的線程棧初始化。

2.rt_hw_stack_init 實(shí)現(xiàn)分析

2.1 向下增長型棧 rt_hw_stack_init 實(shí)現(xiàn)

針對向下增長型的棧,以 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]這個成員是不能寫值的。

1.jpg

stk = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stk, 8); 之后按8字節(jié) 向下對齊,那就 stk 就變成了 96,table[97]、table[98]、table[99]由于字節(jié)對齊就保留了,后續(xù)也不會去使用,至于table[96]用沒用還不知道,我們接著看。

1.jpg

stk -= sizeof(struct stack_frame);,stk 減掉 struct stack_frame 結(jié)構(gòu)大小存儲 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]不會去訪問。

1.jpg

因此無論字節(jié)對齊的時候有沒有保留字節(jié),第一步stk雖然切換到了棧最頂端,但是并不會訪問最頂端的那個成員,所以是安全的!

2.1 向上增長型棧 rt_hw_stack_init 實(shí)現(xiàn)

注意向上增長型棧初始化代碼就不是上面那一份了!上面我們說了針對不同的cpu,會有不同的cpuport.c文件來實(shí)現(xiàn)對應(yīng)的 rt_hw_stack_init,因此我們需要找到向上增長型的cpu對應(yīng)的cpuport.c來分析才行。

rtthread內(nèi)核中,目前僅TI的tms320f28379為向上增長型,對應(yīng)的cpuport.c在 libcpu/ti-dsp/c28x/cpuport.c內(nèi),它實(shí)現(xiàn)的 rt_hw_stack_init 函數(shù)如下:
(不要問我怎么找到的,根據(jù)宏全局搜ARCH_CPU_STACK_GROWS_UPWARD=y能發(fā)現(xiàn)只有ti這顆用的向上增長型! 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);
}

向上增長型就簡單了,直接加就可以了,不過向上增長型字節(jié)對齊采用的是 RT_ALIGN 向上對齊的方式!

擴(kuò)展知識:
此外,關(guān)于棧除了向上增長和向下增長之外,還有一個知識點(diǎn):滿堆棧 和 空堆棧

概念介紹:

滿堆棧不是指堆棧滿了的意思,空堆棧也不是指堆棧空的意思,而是根據(jù)堆棧指針(SP指針)指向的空間是否存有數(shù)據(jù)來決定。

當(dāng)SP指針指向的地址空間沒有存放有效數(shù)據(jù),則稱之為空堆棧;

當(dāng)SP指針指向的地址空間存放有有效數(shù)據(jù),則稱之為滿堆棧。

因此針對滿堆棧,寫入數(shù)據(jù)的流程為先移動SP指針再填寫有效數(shù)據(jù);而對于空堆棧則是先填寫有效數(shù)據(jù)再移動堆棧指針。

由滿堆棧、空堆棧與向上增長堆棧、向下增長堆棧,共可組成四種組合:

向上遞增滿堆棧(滿增)
向下遞增滿堆棧(滿減)
向上遞增空堆棧(空增)
向下遞增空堆棧(空簡)

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 存儲器
    +關(guān)注

    關(guān)注

    38

    文章

    7649

    瀏覽量

    167357
  • cpu芯片
    +關(guān)注

    關(guān)注

    0

    文章

    47

    瀏覽量

    13853
  • Cortex-M4
    +關(guān)注

    關(guān)注

    6

    文章

    99

    瀏覽量

    47189
  • FPU
    FPU
    +關(guān)注

    關(guān)注

    0

    文章

    45

    瀏覽量

    22063
  • RT-Thread
    +關(guān)注

    關(guān)注

    32

    文章

    1406

    瀏覽量

    41929
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點(diǎn)推薦

    RT-Thread自動初始化詳解

    我們知道,在寫裸機(jī)程序時,當(dāng)我們完成硬件初始化后,就需要在主函數(shù)中進(jìn)行調(diào)用。當(dāng)我們使用RT-Thread后,完全不需要這樣做了,我們可以將硬件等自動初始化。 RT-Thread?自動
    的頭像 發(fā)表于 06-25 21:38 ?1.2w次閱讀
    <b class='flag-5'>RT-Thread</b>自動<b class='flag-5'>初始化</b>詳解

    深度剖析 RT-Thread 線程調(diào)度流程

    RT-Thread調(diào)度第一個線程的主要流程分如下:rtthread_startup:RTT的啟動函數(shù),主要負(fù)責(zé)板級驅(qū)動,調(diào)度器,系統(tǒng)線程初始化,啟動調(diào)度的工作
    的頭像 發(fā)表于 06-25 18:24 ?706次閱讀
    深度剖析 <b class='flag-5'>RT-Thread</b> <b class='flag-5'>線程</b>調(diào)度流程

    什么是RT-Thread線程管理看完你就懂了

    () 函數(shù)里添加自己的應(yīng)用程序初始化代碼。線程的管理方式本章前面 2 節(jié)對線程的功能與工作機(jī)制進(jìn)行了概念上的講解,相信大家對線程已經(jīng)不再陌生。本節(jié)將深入到
    發(fā)表于 03-29 06:16

    RT-Thread 踩坑記錄 - 初始化線程時使用局部變量

    前言為了不再CTRL+C,CTRL+V,修改,我開始嘗試手敲代碼。RT-Thread線程可以靜態(tài)初始化,也可以動態(tài)申請內(nèi)存的方式創(chuàng)建靜態(tài)初始化線程
    發(fā)表于 05-13 18:40

    RT-Thread線程簡介

    文章目錄RT-Thread線程簡介源碼分析初始化線程線程脫離啟動
    發(fā)表于 08-24 07:56

    如何對RT-Thread系統(tǒng)進(jìn)行初始化

    RT-Thread是如何啟動的?如何對RT-Thread系統(tǒng)進(jìn)行初始化呢?
    發(fā)表于 11-30 07:54

    RT-Thread自動初始化機(jī)制簡介

    的 main 線程里被調(diào)用執(zhí)行,這個時候硬件環(huán)境和操作系統(tǒng)已經(jīng)初始化完成,可以執(zhí)行應(yīng)用相關(guān)代碼。rt_components_init() 函數(shù)會遍歷通過剩下的其他幾個宏申明的初始化函數(shù)
    發(fā)表于 04-06 18:08

    RT-Thread系統(tǒng)初始化與啟動流程詳細(xì)描述

    系統(tǒng)定時器線程voidrt_application_init ()創(chuàng)建用戶線程詳細(xì)描述RT-Thread 的啟動流程RT-Thread 的啟動流程,大致可以分為四個部分:(1)
    發(fā)表于 08-25 15:15

    如何對RT-Thread系統(tǒng)的線程進(jìn)行初始化

    , rt_uint32_t tick);rt_thread_init函數(shù)用來初始化靜態(tài)線程對象。而線程句柄(或者說
    發(fā)表于 08-30 14:51

    RT-Thread自動初始化原理分析

    ;}這里我們直接就可以使用 printf 進(jìn)行打印,而沒有進(jìn)行一些其它的初始化,參考這個思路引出了 RT-Thread 的自動初始化機(jī)制。RT-Thread 自動
    發(fā)表于 12-05 14:17

    一文詳解RT-Thread自動初始化

    在學(xué)RT-Thread時,經(jīng)常能聽到這個詞:自動初始化。用起來也非常容易,一個宏就解決了,但是原理是什么呢?
    的頭像 發(fā)表于 07-21 10:17 ?8135次閱讀
    一文詳解<b class='flag-5'>RT-Thread</b>自動<b class='flag-5'>初始化</b>

    RT-Thread全球技術(shù)大會:如何使用組件以及自動初始化流程

    RT-Thread全球技術(shù)大會:如何使用組件和自動初始化流程 ? ? ? ? ? 審核編輯:彭靜
    的頭像 發(fā)表于 05-27 15:16 ?1223次閱讀
    <b class='flag-5'>RT-Thread</b>全球技術(shù)大會:如何使用組件以及自動<b class='flag-5'>初始化</b>流程

    RT-Thread自動初始化機(jī)制

    ??在分析之前首先查閱 RT-Thread 的官方文檔 [RT-Thread 自動初始化機(jī)制](https://www.rt-thread.
    的頭像 發(fā)表于 06-17 08:52 ?3256次閱讀
    <b class='flag-5'>RT-Thread</b>自動<b class='flag-5'>初始化</b>機(jī)制

    rt-thread 優(yōu)化系列(六)啟動流程重構(gòu)

    去年此時,筆者剛接觸 rt-thread 的時候,被它的初始化過程深深折服了。第一次打開一個 rt-thread 的項(xiàng)目,竟然沒找到多線程在哪兒初始
    的頭像 發(fā)表于 07-04 15:30 ?2141次閱讀
    <b class='flag-5'>rt-thread</b> 優(yōu)化系列(六)啟動流程重構(gòu)

    RT-Thread使用經(jīng)驗(yàn)分享:鏈表未初始化造成死機(jī)

    最近在開發(fā)調(diào)試基于RT-Thread 的驅(qū)動時,遇到一個比較奇怪的死機(jī)問題,后來經(jīng)過一步步排查,終于發(fā)現(xiàn)是驅(qū)動的鏈表節(jié)點(diǎn)沒有初始化造成的死機(jī)
    的頭像 發(fā)表于 10-08 14:49 ?1501次閱讀
    <b class='flag-5'>RT-Thread</b>使用經(jīng)驗(yàn)分享:鏈表未<b class='flag-5'>初始化</b>造成死機(jī)