RTOS Thread stack and MSP/PSP registers in Cortex-M3

来源:http://www.cnblogs.com/ironx/archive/2016/08/24/5793976.html
-Advertisement-
Play Games

Background 使用Keil RTX RTOS的項目開發過程中,在加入一些新的代碼之後,發現線上程們被創建並被啟動之後,程式就跑飛了。 藉助Keil的RTOS debug視窗,發現有其中2個線程有stack overflow的現象。 於是開始思考RTOS thread stack size的設 ...


Background

使用Keil RTX RTOS的項目開發過程中,在加入一些新的代碼之後,發現線上程們被創建並被啟動之後,程式就跑飛了。

藉助Keil的RTOS debug視窗,發現有其中2個線程有stack overflow的現象。

於是開始思考RTOS thread stack size的設置問題。

以前一直就對有了RTOS之後,線程棧和內核棧是個什麼情況。Cortex-M3的MSP和PSP該如何使用,這些都不是很清楚。

正好藉此機會,好好研究一番。

 

本文基於以下開發環境:

Cortex-M3,Keil MDK 5。

C memory Modle

先上一張圖,看看一般意義上的無OS情況下,ARM C語言執行環境下,RAM的佈局。明顯這張圖是基於“向下增長”的棧結構來設計的。

從上圖可以看到:

RW空間,位於RAM的最低地址區域。用來存放,已經被初始化了的,可讀寫的,全局變數的值;(提個問題,到底什麼叫全局變數,什麼叫全局變數的內容?)

ZI空間,位於RW的上方。用來存放,未被初始化的,可讀寫的全局變數的值;

Heap空間,位於ZI的上方。c函數malloc和calloc等記憶體分配函數,會從這塊區域裡面取一塊記憶體區域,給函數的調用者;

Stack空間,位於RAM的最上方。C函數的自動變數(也叫局部變數),以及函數返回地址,會被保存在這裡。

Stack空間從高位向低位增長,Heap從低位向高位增長。所以如果控制不好,會出現Heap和Stack overlap的情況。

可以參考:http://stackoverflow.com/questions/39113658/when-does-malloc-return-null-in-a-bare-metal-environment

那麼這個c memory model是怎麼形成的呢?

當我們寫好了一個c程式,開始build這個程式。

這個程式會被預處理、編譯、鏈接,形成一份image。編譯器會分辨出:代碼指令,常量,已初始化的可讀寫全局變數,未初始化的可讀寫全局變數,自動變數,記憶體分配函數。

鏈接腳本(link script)會告訴鏈接器,在鏈接的時候這些東西在flash裡面放哪裡,在RAM裡面放哪裡。

那麼我們關心的棧,是在哪裡設置棧頂是在RAM的最高位,而heap在ZI的上面呢?

CPU裡面有個SP寄存器,又叫棧指針寄存器,指向程式運行時棧的實時位置。

往棧裡面PUSH東西啦,SP寄存器的數值就減小;從棧裡面POP東西啦,SP寄存器的數值就增加。

在CPU剛開始複位,開始從複位中斷向量執行第一條指令的時候,都是彙編,但我們也得先把CPU初始化、棧指針初始化啊,這樣後面的c程式才能開始跑。

一般在這裡,我們都是通過彙編指令設置SP寄存器為RAM的最高地址,也就是指定了棧頂在哪裡。

而Heap底部,則是鏈接器自動根據RW/ZI空間大小計算出來的,等於ZI空間的頂部。

 

Keil MDK下Cortex-M3的C Memory Model, without RTOS

那麼在Keil MDK下,Cortex-M3的C memory model又是個什麼樣子呢。

為了說明,先上圖。

在不跑RTOS的情況下,這個時候,整個C程式都只會用到main stack,為什麼?請去翻翻cortex-m3 技術手冊。

當然高級一點,可以自己去設置process stack,然後讓用戶程式在process stack裡面跑,讓中斷函數再main stack裡面跑。

先看沒有RTOS,並且代碼裡面調用了malloc或者calloc這類記憶體分配函數的情況:

      +--------+   Last Address of RAM  
      |  not   |
| used | +--------+ MSP RAM | main |
| stck | | | +--------+ Heal_limit | ^ | | | | | Heap | +--------+ | ZI | +--------+ | RW | +========+ First Address of RAM

從圖上可以看到RAM的頂部有一段空間,是沒有被c程式用到的,相當於浪費掉了。

在這段空間的下麵才是Main stack,MSP指向了這段空間的最上面。

Heap 在main stack的下麵。

startup.s裡面設置了heap和main stack的size,Keil toolchain會計算heap的起始地址,main stack的起始地址,防止出現heap和stack overlap的情況。

設置見下圖的“Stack Configuration”和“Heap Configuration”,預設都是0x400 = 1k bytes。

 

 

以後c程式,就從heap裡面去取memory,往main stack裡面存自動變數和函數返回地址。

註意,如果你的c代碼裡面沒有調用malloc這類函數,那麼是不會最後生成的image,去看map file,是不會有heap這段空間的。

我們來看看沒有RTOS的時候,並且沒有調用malloc這類函數,最後image的map file是什麼樣子的。沒錯,沒有heap,只有stack。

    Execution Region RW_IRAM1 (Base: 0x20000000, Size: 0x00000418, Max: 0x00002000, ABSOLUTE)

    Base Addr    Size         Type   Attr      Idx    E Section Name        Object

    0x20000000   0x00000004   Data   RW            4    .data               main.o
    0x20000004   0x00000014   Data   RW          236    .data               system_gd32f1x0.o
    0x20000018   0x00000400   Zero   RW         3328    STACK               startup_gd32f1x0.o

那我們再來看看沒有RTOS的時候,有調用malloc這類函數,最後image的map file是什麼樣子的。看到沒有,有heap,也有stack。並且size都是在之前的startup.s中設置的。

    Execution Region RW_IRAM1 (Base: 0x20000000, Size: 0x00000820, Max: 0x00002000, ABSOLUTE)

    Base Addr    Size         Type   Attr      Idx    E Section Name        Object

    0x20000000   0x00000004   Data   RW            4    .data               main.o
    0x20000004   0x00000014   Data   RW          239    .data               system_gd32f1x0.o
    0x20000018   0x00000004   Data   RW         3387    .data               mc_w.l(mvars.o)
    0x2000001c   0x00000004   Data   RW         3388    .data               mc_w.l(mvars.o)
    0x20000020   0x00000400   Zero   RW         3332    HEAP                startup_gd32f1x0.o
    0x20000420   0x00000400   Zero   RW         3331    STACK               startup_gd32f1x0.o

再來看這段調用malloc代碼經過Keil編譯之後,統計的code大小,data大小。

看到沒有RW size = 2080 bytes。而上面顯示的所有RAM中被用到的空間大小為0x00000820 = 2080 bytes。

是的,ZI Data包含了stack和heap空間的。所以keil編譯出來的結果,就是最後ram占用的空間大小。而這個和c memory model裡面講的是不一樣的。

==============================================================================

    Total RO  Size (Code + RO Data)                 1400 (   1.37kB)
    Total RW  Size (RW Data + ZI Data)              2080 (   2.03kB)
    Total ROM Size (Code + RO Data + RW Data)       1432 (   1.40kB)

==============================================================================

Keil MDK下Cortex-M3的C Memory Model, with Keil RTX RTOS

在有了RTOS之後,就多了RTOS內核和線程。RTOS內核代碼,中斷函數,以及啟動代碼,使用main stack。線程使用process stack。

整個C程式就有2個棧指針寄存器,MSP/PSP。MSP指向main stack的top address,PSP指向線程stack的top address。

於是就有了下麵的圖。

可以看到heap在main stack的下麵,process stack在heap的下麵。

process stack其實是RTOS代碼裡面的一個全局數組。

每創建一個線程,就從數組裡面拿一塊空間作為這個線程的棧空間。

這個數組的大小,由RTX_Conf_CM.C裡面的stack number,以及每個stack的棧大小這兩者的乘積決定。

  1. 當從線程陷入到RTOS內核時,就使用MSP;
  2. 當在RTOS內核中,需要從線程1調度掉線程2時,則修改PSP,將其值從線程1的棧頂,修改到線程2的棧頂。
      +--------+   Last Address of RAM  
      |  not   |
| used | +--------+ MSP RAM | main |
| stck | | | +--------+ Heal_limit | ^ | | | | | Heap | +--------+ PSP
| Process|
| Stack |
| |
+--------+ | ZI | +--------+ | RW | +========+ First Address of RAM
我們再來看看有RTOS的時候,map file是什麼樣子的吧。因為內容比較多,只截取了其中一部分:
Execution Region RW_IRAM1 (Base: 0x20000000, Size: 0x000018a8, Max: 0x00002000, ABSOLUTE, COMPRESSED[0x00000084])

    Base Addr Size Type Attr Idx E Section Name Object
    0x2000016c   0x00000001   Data   RW         7640    .data               RTX_CM3.lib(hal_cm.o)
    0x2000016d   0x00000003   PAD
    0x20000170   0x00000008   Data   RW         7684    .data               RTX_CM3.lib(rt_robin.o)
    0x20000178   0x00000004   Data   RW         8029    .data               mc_w.l(stdout.o)
    0x2000017c   0x0000003c   Zero   RW          250    .bss                pse_hostcomm.o
    0x200001b8   0x000001b0   Zero   RW          484    .bss                pse_pwrmgmt.o
    0x20000368   0x00000010   Zero   RW         6324    .bss                thread.o
    0x20000378   0x00000010   Zero   RW         6325    .bss                thread.o
    0x20000388   0x00000010   Zero   RW         6326    .bss                thread.o
    0x20000398   0x00000020   Zero   RW         6401    .bss                rtx_conf_cm.o
    0x200003b8   0x00000110   Zero   RW         6402    .bss                rtx_conf_cm.o
    0x200004c8   0x00000c90   Zero   RW         6403    .bss                rtx_conf_cm.o
    0x20001158   0x00000250   Zero   RW         6404    .bss                rtx_conf_cm.o
    0x200013a8   0x00000084   Zero   RW         6405    .bss                rtx_conf_cm.o
    0x2000142c   0x00000014   Zero   RW         6406    .bss                rtx_conf_cm.o
    0x20001440   0x00000034   Zero   RW         7141    .bss                RTX_CM3.lib(rt_task.o)
    0x20001474   0x00000030   Zero   RW         7361    .bss                RTX_CM3.lib(rt_list.o)
    0x200014a4   0x00000004   PAD
    0x200014a8   0x00000400   Zero   RW         6457    STACK               startup_gd32f1x0.o


==============================================================================

這個裡面標STACK的,其實是main stack,採用預設值為0x400. 而下麵這段則是所有線程棧空間的總和:

 0x200004c8   0x00000c90   Zero   RW         6403    .bss                rtx_conf_cm.o

 

Keil的統計數據如下,其中RW size剛好等於上面所占的RAM空間大小:Size = 0x000018a8 = 6312 bytes。也就是所Keil給出的結果,RW data + ZI data就是最後代碼運行時占用的RAM大小,包含了所有的stack以及RW/ZI data空間。
==============================================================================

    Total RO  Size (Code + RO Data)                38196 (  37.30kB)
    Total RW  Size (RW Data + ZI Data)              6312 (   6.16kB)
    Total ROM Size (Code + RO Data + RW Data)      38328 (  37.43kB)

==============================================================================

 





您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 首先根據書中講解的實例代碼(標準實例來源於UCOS ii經典教程 邵貝貝) 並且在另一個任務中進行消息發送,如下代碼 無論是斷點調試,還是從輸出的調試信息中都可以確定每次都有消息發送,但是在OSMboxPend中始終沒有接收到消息導致err一直等於OS_TIMEOUT而無法進行正確消息處理 但是只要 ...
  • 最近公司團隊的項目用到ESP8266,使用的是stm32c8t6晶元,軟體使用MDK,於是寫下調試過程中的一些註意點。具體代碼和涉及思路不能外泄,這裡只給出我們調試過程中的註意的方面。 關於ESP8266,我們首先做一些說明,這些都是我們團隊在調試過程中總結出來的,都是經過實際的測試,而不是談論或是 ...
  • 簡要複習一下ARM9中斷控制器的控制過程: 1.首先能識別觸發的中斷(對應中斷源必須打開,然後查詢當前中斷狀態寄存器),硬體會操控PC跳到中斷向量入口(IRQ_HANDLE,硬體控制的只要是IRQ中斷類型就會進入),在中斷跳轉函數裡面保存現場(保存R0等等工作寄存器)--跳到服務函數(裡面進行中斷源 ...
  • 文件描述符 可以理解為linux跟蹤打開文件,而分配的一個數字,這個數字有點類似c語言操作文件時候的句柄,通過句柄就可以實現文件的讀寫操作 用戶可以自定義文件描述符範圍是:3 num,這個最大數字,跟用戶的:ulimit –n 定義數字有關係,不能超過最大值 查看系統文件描述符最大值 linux系統 ...
  • Centos6.2代碼 CentOS 6.0沒有預設沒有裝語言支持(Language Support),因此很不方面。 終於發現了有效的方法: su root yum install "@Chinese Support" exit 最後一步:logout,註意是註銷,然後再次登錄此用戶。 當然這裡安... ...
  • 在ubuntu下網路管理器Network Manager莫名奇妙出現無法連接無線網的情況,於是昨天就開始著手解決這一問題: 一 :卸載 1.第一步卸載Network-Manager (具體字母的大小寫我沒註意,如果Network-Manager不能成功,那就換成network-manager試試,卸 ...
  • 熟悉 CDN 行業主流技術的朋友應該都比較清楚,雖然 Nginx 近幾年發展的如日中天,但是基本上沒有直接使用它自帶的 proxy_cache 模塊來做緩存的,原因有很多,例如下麵幾個: 不支持多盤 不支持裸設備 大文件不會切片 大文件的 Range 請求表現不盡如人意 Nginx 自身不支持合併回 ...
  • 最近開始看操作系統的書(本身不是這個方向的),單看書本管程還是不能完全理解,現在簡單總結一下吧。 1.管程 (英語:Monitors,也稱為監視器) 是一種程式結構,結構內的多個子程式(對象或模塊)形成的多個工作線程互斥訪問共用資源。這些共用資源一般是硬體設備或一群變數。管程實現了在一個時間點,最多 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...