嵌入式作業系統漫議 任務呼叫棧

2021-10-19 07:53:59 字數 2020 閱讀 9044

在進行嵌入式系統設計時,很重要的乙個方面是決定整個系統由幾個任務構成、任務的優先順序以及任務呼叫棧(call stack)的大小。本篇主要介紹呼叫棧的相關概念。

呼叫棧是用於儲存任務執行時的臨時變數的記憶體區域,以堆疊的形式管理。圖1是乙個呼叫棧的結構示意圖。呼叫棧中主要儲存函式的形參、區域性變數和返回位址。因此呼叫棧的大小主要根據該任務函式的區域性變數所佔空間的大小和函式的呼叫深度來決定。

在很多mcu架構(包括arm系列)中,呼叫棧按記憶體位址遞減的方向生長,也即棧底在高位址,棧頂在低位址。每次函式呼叫時,系統將返回位址、暫存器值、函式引數等壓入堆疊,同時在堆疊中為區域性變數留出空間,而這構成乙個棧幀,對應一次函式呼叫。因此,在多次函式呼叫時,多個棧幀從高位址區往低位址區排列,最近的棧幀在低位址區,靠近棧頂。

圖 1 呼叫棧結構

對呼叫棧的操作有壓棧(push)和彈棧(pop)兩種。其中的資料遵循先進後出(filo)的原則。圖2是乙個壓棧和彈棧的操作示意圖。棧指標總是指向堆疊中下乙個可以儲存的位置,當壓入0x5678時,棧指標自動往下移乙個字(32位mcu的話,4個位元組)。繼續壓入0x9abc,棧指標繼續下移乙個字。此時執行pop操作,則後壓入的0x9abc先被彈出,棧指標上移乙個字。此時繼續執行pop,則0x5678被彈出。因此在堆疊中資料是先進後出。

圖 2 棧操作示意圖

呼叫側:

function1(prt1, usc1, usc2, 1, 0)

movs r3,#0x01

mov  r2,r11

mov  r1,r8

str  r0,[sp,#0x00]

mov  r0,r5

bl.w function1 (0x08006a48)

被呼叫側:

function1(void *phead, uint16_t usv1, uint16_t usv2, uint8_t ucv3, uint8_t ucv4)

push   

mov     r4,r0

mov     r5,r1

mov     r7,r2

mov     r6,r3

ldr     r8,[sp,#0x18]

在arm系列mcu中,提供了13個通用暫存器(r0-r12)。在函式呼叫時,編譯器一般優先使用這些通用暫存器來傳遞函式引數和儲存區域性變數。因此在壓棧時,用於傳遞函式引數的暫存器是不需要壓入呼叫棧的。下面示例的彙編**是keil編譯的乙個函式呼叫的引數傳遞過程。

這是呼叫乙個有5個引數的函式(function1)的呼叫側和被呼叫側與函式呼叫相關的**片段。通過分析前面的**,在呼叫側,r11存有usc2,r8存有usc1,r5存有ptr1,r0被設為0。在呼叫函式function1時,從左到右將ptr1、usc1、usc2、1分別儲存到r0-r3之中,而將最後乙個引數0壓入堆疊。而在被呼叫側,則首先將r4-r8及lr(返回位址暫存器)壓入堆疊,然後將r0-r3分別賦給了r4-r7(注意並不是按順序賦值的,r2賦給了r7,r3賦給了r6),然後將最後乙個引數從堆疊中取出,賦給r8。此處,有幾點需要注意的,一是將多少個暫存器壓入堆疊取決於被呼叫函式中會用到幾個暫存器,只需要將可能修改的暫存器壓棧即可。二是從堆疊取數的時候,因為是取堆疊中間的乙個數,因此通過從棧指標的偏移量來計算。因為壓入了24個位元組,故而需要將sp偏移24個位元組來取得最後乙個函式引數。還有,因為arm的堆疊是向下生長的,因此需要加24個位元組。

呼叫棧的大小在任務建立時指定,而且在任務生存期內不再變化。如果指定乙個過大的呼叫棧,會造成記憶體的浪費,而在嵌入式系統中,記憶體資源是非常寶貴的;但如果指定的呼叫棧過小,則可能導致堆疊越界的問題,會導致呼叫棧中的內容被覆蓋。呼叫棧越界會表現出各種各樣稀奇古怪的問題,比如系統崩潰、變數值發生了莫名其妙的改變等等。而且問題的發生可能不具有確定性。因此這種問題的定位和解決往往會花費大量時間。因此為了防止和快速定位堆疊越界問題,從晶元、作業系統到整合開發環境提供了各種各樣的除錯工具。

嵌入式作業系統概述

實時作業系統 rtos 設計成提供乙個對真實世界的事件的及時響應 timely response 出現在真實世界中的事件可能有乙個時間限制 deadline 在此期限之前,實時 嵌入式系統必須確保在有限時間內對此事件做出相應的響應。根據相應事件的時間限制,嵌入式實時作業系統可以分為兩類 1 硬實時嵌...

嵌入式作業系統簡介

嵌入式作業系統簡介 商用型系統 vxworks 半開源 wince 半開源 免費型 linux ucosii 只適用於控制,不適合遠端,不自帶網路協議棧 linux模仿unix unix是在於1969年在at t的貝爾實驗室開發 備註 丹尼斯.里奇 unix之父 c語言之父 gpl協議 gpl同其它...

嵌入式作業系統uCOSII

2.4 常用資料結構 2.4.1 程式控制塊 本質上是結構體 typedef struct tbctcb 2.4.2 控制塊的組織 鍊錶 typedef struct tcbtcb 位圖!3 ucossii中的任務 任務組成 任務程式 函式 任務堆疊和任務控制塊 具有私有空間的任務叫做程序,沒有私有...