日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當(dāng)前位置:首頁(yè) > 嵌入式 > 嵌入式客棧
[導(dǎo)讀]關(guān)注、星標(biāo)嵌入式客棧,干貨及時(shí)送達(dá) [導(dǎo)讀] 從這篇文章開(kāi)始,將會(huì)不定期更新關(guān)于嵌入式C語(yǔ)言編程相關(guān)的個(gè)人認(rèn)為比較重要的知識(shí)點(diǎn),或者踩過(guò)的坑。 為什么要深入理解棧?做C語(yǔ)言開(kāi)發(fā)如果棧設(shè)置不合理或者使用不對(duì),棧就會(huì)溢出,溢出就會(huì)遇到無(wú)法預(yù)測(cè)亂飛現(xiàn)象

關(guān)注、星標(biāo)嵌入式客棧,干貨及時(shí)送達(dá)

[導(dǎo)讀] 從這篇文章開(kāi)始,將會(huì)不定期更新關(guān)于嵌入式C語(yǔ)言編程相關(guān)的個(gè)人認(rèn)為比較重要的知識(shí)點(diǎn),或者踩過(guò)的坑。

為什么要深入理解棧?做C語(yǔ)言開(kāi)發(fā)如果棧設(shè)置不合理或者使用不對(duì),棧就會(huì)溢出,溢出就會(huì)遇到無(wú)法預(yù)測(cè)亂飛現(xiàn)象。所以對(duì)棧的深入理解是非常重要的。

注:動(dòng)畫(huà)如果看不清楚可以電腦看更清晰

啥是棧

先來(lái)看一段動(dòng)畫(huà):

沒(méi)有比這個(gè)更直觀的啦,棧是一種受限的數(shù)據(jù)結(jié)構(gòu)模型,其數(shù)據(jù)總是只能在頂部追加,利用一個(gè)指針進(jìn)行索引,頂端叫棧頂,相對(duì)的一端底部稱(chēng)為棧底。棧是一種LIFO后入先出的數(shù)據(jù)結(jié)構(gòu)。

棧就兩種操作:

  • PUSH,壓棧,向棧內(nèi)加入數(shù)據(jù),
  • POP,出棧

再進(jìn)一步探討:

首先將棧與堆分清,從看到這篇文章開(kāi)始,我建議你不要把堆和棧連在一起叫,棧是棧,堆是堆,這是兩回事,別混為一談?。ǘ驯疚牟簧钊胗懻摚?/p>

從C/C++編程語(yǔ)言的角度來(lái)看:

  • 相同點(diǎn):都是一片內(nèi)存區(qū),在鏈接時(shí)指定棧區(qū)/堆區(qū)的位置以及大小。

  • 不同點(diǎn):

    • 棧:由編譯器分配,存放函數(shù)的參數(shù)值,局部變量,寄存器組(不同的單片機(jī)/處理器各有不同)、函數(shù)調(diào)用參數(shù)傳遞、中斷異常產(chǎn)生時(shí)須保存處理器狀態(tài)的寄存器值等
    • 堆:由程序員分配釋放,對(duì)于C而言,malloc、realloc/free進(jìn)行分配/釋放,對(duì)C++而言,由new/delete分配/釋放。

為啥用

棧這個(gè)數(shù)據(jù)模型的應(yīng)用價(jià)值是什么呢?先來(lái)看一下單片機(jī)內(nèi)部的可能有哪些棧應(yīng)用?以STM32為例,參考IAR C/C++ Development Guide,P207

處理器模式 建議段名 描述
Supervisor SVC_STACK 操作系統(tǒng)棧
IRQ IRQ_STACK 通用(IRQ)中斷處理程序的堆棧。
FIQ FIQ_STACK 用于高速(FIQ)中斷處理程序的堆棧。
Undefined UND_STACK 堆棧用于未定義的指令中斷。支持硬件協(xié)處理器和指令集擴(kuò)展的軟件仿真。
Abort ABT_STACK 用于指令獲取和數(shù)據(jù)訪問(wèn)存儲(chǔ)器中止中斷處理程序的堆棧。

如果使用RTOS還有任務(wù)棧,如果是Linux,其內(nèi)核線程同樣也需要棧的支持,等等這一切的一切棧,其本質(zhì)上都是利用了棧數(shù)據(jù)模型的LIFO后入先出的特性,一個(gè)典型應(yīng)用場(chǎng)景就是比如做一件事情做到一半而要轉(zhuǎn)而去做另外一件事,對(duì)于芯片編程而言,就需要將當(dāng)前的工作做個(gè)暫存,等另外一件事情做完了,再接著回來(lái)繼續(xù)做。那么怎么做到呢,以一個(gè)中斷處理為例,要記住當(dāng)前的工作態(tài)有哪些信息需要暫存呢?PC指針,局部變量等就被壓入棧,再將中斷服務(wù)程序地址導(dǎo)入PC指針,進(jìn)而去執(zhí)行中斷服務(wù)程序,待中斷處理完畢,在將棧里的內(nèi)容按照后入先出彈出到對(duì)應(yīng)的寄存器就恢復(fù)了原程序的現(xiàn)場(chǎng),進(jìn)而繼續(xù)執(zhí)行。

怎么用

棧在哪里定義大小,定多大合適?這可能很多剛接觸單片機(jī)開(kāi)發(fā)的同學(xué)不是太清楚,下面就將比較常見(jiàn)的IAR開(kāi)發(fā)環(huán)境為例如何定義棧定義棧大小的地方說(shuō)明一下,這里以IAR8.4.1為例,有兩種方式可以進(jìn)行棧大小設(shè)置。

IDE設(shè)置

為了更加清楚明了,制作了一個(gè)GIF操作展示視頻,在stack/heap中就可以設(shè)置了,其中stack用于設(shè)置棧區(qū)大小,heap用于設(shè)置堆大小。

這個(gè)demo中設(shè)置了其棧的大小為0x200,堆的大小為0x400,全編譯后,檢查map文件就印證了棧/堆的大小如預(yù)期所修改。

鏈接配置文件

其實(shí)對(duì)于比較熟悉的開(kāi)發(fā)人員,上一種方式并非推薦用法。用鏈接配置文件將具有更好的靈活性,比如可以指定一個(gè)段的對(duì)齊方式,存儲(chǔ)位置,某個(gè)符號(hào)的存儲(chǔ)位置等等。這里同樣為了直觀也做了一個(gè)GIF動(dòng)畫(huà),介紹如何通過(guò)鏈接文件進(jìn)行棧/堆的大小配置。


其最終的效果也一樣如預(yù)期將棧區(qū)的大小設(shè)置好了。

棧溢出

這里為了比較容易的展示棧溢出的問(wèn)題,在main函數(shù)利用遞歸方法計(jì)算階乘,代碼如下:

#include <stdio.h>
#include "main.h"
static uint32_t spSatte[200];
static uint32_t spIndex = 0;
/*為什么要用浮點(diǎn)數(shù),因?yàn)閿?shù)據(jù)非常大整型很快就會(huì)溢出*/
float factorial(uint32_t n)
{
    uint32_t sp = __get_MSP();    
    /*記錄棧指針的變化情況*/
    spSatte[spIndex++] = sp;
    if(n==0 || n==1)
        return 1;
    else
        return (float)n*factorial(n-1);
}

int main(void)
{
    float  x = 0;
    uint32_t  n = 20;
    printf("stack test:\n");
    x = factorial(n);
    /*打印棧指針變化情況*/
    for(int i = 0;i<spIndex;i++)
        printf("MSP->%08X\n",spSatte[i]);
    
    /*打印階乘結(jié)果*/
    printf("factorial(%d)=%f\n",n,x);    
    while (1)
    {
    }
}

為方便觀察,將stm32f407xx_flash.icf 將棧改為256字節(jié)

/*stm32f407xx_flash.icf 將棧改為256字節(jié)*/
define symbol __ICFEDIT_size_cstack__ = 0x200;
define symbol __ICFEDIT_size_heap__   = 0x200;

全編譯后通過(guò)map文件來(lái)看下棧/堆的分配情況:

"P2", part 3 of 3:                          0x400
  CSTACK                      0x2000'05d8   0x200  <Block>
    CSTACK           uninit   0x2000'05d8   0x200  <Block tail>
  HEAP                        0x2000'07d8   0x200  <Block>
    HEAP             uninit   0x2000'07d8   0x200  <Block tail>
                            - 0x2000'09d8   0x400

直觀些,翻譯成下圖,CSTACK段分配在0x2000 0280-0x2000 0480,堆分配在0x2000 0480-0x2000 0680。

圖為什么沒(méi)有將0x2000 07D8畫(huà)在棧區(qū)呢?通過(guò)調(diào)試發(fā)現(xiàn),這個(gè)字空間沒(méi)有用做棧的實(shí)際存儲(chǔ)。將工程設(shè)置成simulation模式,debug進(jìn)入main.o勾選掉,我們來(lái)計(jì)算20的階乘,來(lái)具體看一下:

對(duì)這個(gè)動(dòng)圖解讀一下:

  • 進(jìn)入復(fù)位是,SP_main為0x200007D8,指向棧底,為空棧。那么這是怎么實(shí)現(xiàn)的呢?
__vector_table                ;向量表
DCD sfe(CSTACK) ;這條命令會(huì)將程序的CSTACK起始地址裝載給SP_main
DCD Reset_Handler ; Reset Handler復(fù)位向量
  • 前面說(shuō)0x200007D8并沒(méi)有用到,怎么證明呢,在函數(shù)進(jìn)入mian時(shí),第一次壓棧的情況如下:

  • 可見(jiàn)STM32棧的增長(zhǎng)方向是向下增長(zhǎng)的,也即頂在小地址端一側(cè)
  • 棧存儲(chǔ)元素是四字節(jié)對(duì)齊的,因?yàn)镾TM32的字長(zhǎng)是字節(jié),如果深入想想,如果不是司字節(jié)對(duì)齊會(huì)怎么樣?留給感興趣的思考一下。
  • 0x200007D8--0x200007DB 這個(gè)字存儲(chǔ)單元并不是棧的有效存儲(chǔ)空間。

棧的變化情況:

stack test:
MSP->200007A8 
MSP->20000790
MSP->20000778
MSP->20000760
MSP->20000748
MSP->20000730
MSP->20000718
MSP->20000700
MSP->200006E8
MSP->200006D0
MSP->200006B8
MSP->200006A0
MSP->20000688
MSP->20000670
MSP->20000658
MSP->20000640
MSP->20000628
MSP->20000610
MSP->200005F8
MSP->200005E0
factorial(20)=2432902023163674771.785700 /*結(jié)算結(jié)果與用計(jì)算器一致*/

每調(diào)用一次階乘函數(shù),棧就壓入4個(gè)字,由上面還可以看到第20次進(jìn)入時(shí),棧指針為0x200005E0,如果再壓入4個(gè)字棧指針會(huì)變成0x200005C8,是這樣嗎,結(jié)果還對(duì)嗎?將n改為21編譯運(yùn)行,來(lái)看一看:

看到了吧,驚喜來(lái)了,棧溢出了,程序已經(jīng)不聽(tīng)話了,完全不知道在干嘛了。所以棧溢出的后果是極端危險(xiǎn)的,完全無(wú)法預(yù)期,程序會(huì)帶來(lái)什么后果。

總結(jié)一下

  • 棧是一種LIFO后入先出的數(shù)據(jù)結(jié)構(gòu)模型,是C/C++程序運(yùn)行時(shí)基礎(chǔ),沒(méi)這個(gè)棧,C/C++玩不轉(zhuǎn)
  • 棧在嵌入式編程領(lǐng)域隨處可見(jiàn),比如C棧,中斷棧、異常棧、任務(wù)棧等等,但其基本工作原理都一樣。支持兩種基本數(shù)據(jù)操作:壓棧、出棧。
  • 棧溢出程序的結(jié)果無(wú)法預(yù)期,所以合理的設(shè)置棧區(qū)大小是個(gè)永恒的話題,過(guò)大則浪費(fèi)內(nèi)存,過(guò)小則程序會(huì)飛。
  • 嵌入式編程遞歸函數(shù)要慎用,個(gè)人建議不用。比如IEC61508 功能安全標(biāo)準(zhǔn)中強(qiáng)行規(guī)定不可使用遞歸函數(shù)。
  • STM32中__get_MSP可以得到當(dāng)前棧指針的值,據(jù)此可以做一定程度的棧溢出保護(hù)措施。防止程序跑飛。
  • 通過(guò)上面遞歸調(diào)用測(cè)試,還可以得到一個(gè)啟示,嵌入式編程函數(shù)嵌套的層級(jí)不宜過(guò)深,過(guò)深則需要相對(duì)較大的棧開(kāi)銷(xiāo)。
  • .......
點(diǎn)擊留言/查看留言

END

果喜歡右下點(diǎn)個(gè)在看,也會(huì)讓我倍感鼓舞

往期精彩推薦




▲深度解析U-Boot網(wǎng)絡(luò)實(shí)現(xiàn)(長(zhǎng)篇好文)
讀U-Boot源碼-C語(yǔ)言編程大法總結(jié)篇一
讀U-Boot源碼-C語(yǔ)言編程技巧總結(jié)篇二
基于Buildroot的Linux系統(tǒng)構(gòu)建之根文件系統(tǒng)
手把手教系列之移動(dòng)平均濾波器C實(shí)現(xiàn)
手把手教系列之IIR數(shù)字濾波器設(shè)計(jì)實(shí)現(xiàn)
手把手教系列之梳狀濾波器設(shè)計(jì)實(shí)現(xiàn)

關(guān)注置頂:掃描左下二維碼關(guān)注公眾號(hào)加星

討論加群:掃描右下二維碼添加,發(fā)送“加群”

關(guān)注

加群

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專(zhuān)欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

LED驅(qū)動(dòng)電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: 驅(qū)動(dòng)電源

在工業(yè)自動(dòng)化蓬勃發(fā)展的當(dāng)下,工業(yè)電機(jī)作為核心動(dòng)力設(shè)備,其驅(qū)動(dòng)電源的性能直接關(guān)系到整個(gè)系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動(dòng)勢(shì)抑制與過(guò)流保護(hù)是驅(qū)動(dòng)電源設(shè)計(jì)中至關(guān)重要的兩個(gè)環(huán)節(jié),集成化方案的設(shè)計(jì)成為提升電機(jī)驅(qū)動(dòng)性能的關(guān)鍵。

關(guān)鍵字: 工業(yè)電機(jī) 驅(qū)動(dòng)電源

LED 驅(qū)動(dòng)電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個(gè)照明設(shè)備的使用壽命。然而,在實(shí)際應(yīng)用中,LED 驅(qū)動(dòng)電源易損壞的問(wèn)題卻十分常見(jiàn),不僅增加了維護(hù)成本,還影響了用戶(hù)體驗(yàn)。要解決這一問(wèn)題,需從設(shè)計(jì)、生...

關(guān)鍵字: 驅(qū)動(dòng)電源 照明系統(tǒng) 散熱

根據(jù)LED驅(qū)動(dòng)電源的公式,電感內(nèi)電流波動(dòng)大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關(guān)鍵字: LED 設(shè)計(jì) 驅(qū)動(dòng)電源

電動(dòng)汽車(chē)(EV)作為新能源汽車(chē)的重要代表,正逐漸成為全球汽車(chē)產(chǎn)業(yè)的重要發(fā)展方向。電動(dòng)汽車(chē)的核心技術(shù)之一是電機(jī)驅(qū)動(dòng)控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機(jī)驅(qū)動(dòng)系統(tǒng)中的關(guān)鍵元件,其性能直接影響到電動(dòng)汽車(chē)的動(dòng)力性能和...

關(guān)鍵字: 電動(dòng)汽車(chē) 新能源 驅(qū)動(dòng)電源

在現(xiàn)代城市建設(shè)中,街道及停車(chē)場(chǎng)照明作為基礎(chǔ)設(shè)施的重要組成部分,其質(zhì)量和效率直接關(guān)系到城市的公共安全、居民生活質(zhì)量和能源利用效率。隨著科技的進(jìn)步,高亮度白光發(fā)光二極管(LED)因其獨(dú)特的優(yōu)勢(shì)逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關(guān)鍵字: 發(fā)光二極管 驅(qū)動(dòng)電源 LED

LED通用照明設(shè)計(jì)工程師會(huì)遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

關(guān)鍵字: LED 驅(qū)動(dòng)電源 功率因數(shù)校正

在LED照明技術(shù)日益普及的今天,LED驅(qū)動(dòng)電源的電磁干擾(EMI)問(wèn)題成為了一個(gè)不可忽視的挑戰(zhàn)。電磁干擾不僅會(huì)影響LED燈具的正常工作,還可能對(duì)周?chē)娮釉O(shè)備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來(lái)解決L...

關(guān)鍵字: LED照明技術(shù) 電磁干擾 驅(qū)動(dòng)電源

開(kāi)關(guān)電源具有效率高的特性,而且開(kāi)關(guān)電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機(jī)重量也有所下降,所以,現(xiàn)在的LED驅(qū)動(dòng)電源

關(guān)鍵字: LED 驅(qū)動(dòng)電源 開(kāi)關(guān)電源

LED驅(qū)動(dòng)電源是把電源供應(yīng)轉(zhuǎn)換為特定的電壓電流以驅(qū)動(dòng)LED發(fā)光的電壓轉(zhuǎn)換器,通常情況下:LED驅(qū)動(dòng)電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: LED 隧道燈 驅(qū)動(dòng)電源
關(guān)閉