1. 程式人生 > >MDK的程式設計過程和變數儲存位置

MDK的程式設計過程和變數儲存位置

1 參考書籍《零死角玩轉STM32-F429》

2 程式設計過程

  首先我們簡單瞭解下 MDK 的編譯過程,它與其它編譯器的工作過程是類似的,該過程見圖 51-1。


  (1) 編譯, MDK 軟體使用的編譯器是 armcc armasm,它們根據每個 c/c++和彙編原始檔編譯成對應的以“.o”為字尾名的物件檔案(Object Code,也稱目標檔案),其內容主要是從原始檔編譯得到的機器碼,包含了程式碼、資料以及除錯使用的資訊;

(2) 連結,連結器 armlink 把各個.o 檔案及庫檔案連結成一個映像檔案“.axf”或“.elf”:

(3) 格式轉換,一般來說 Windows Linux

系統使用連結器直接生成可執行映像檔案 elf後,核心根據該檔案的資訊載入後,就可以執行程式了,但在微控制器平臺上,需要把該檔案的內容載入到晶片上,所以還需要對連結器生成的 elf 映像檔案利用格式轉換器fromelf 轉換成“.bin”或“.hex”檔案,交給下載器下載到晶片的 FLASH ROM

3 程式的組成,儲存和執行

  根據每一個程式的編譯提示,如下圖


  在工程的編譯提示輸出資訊中有一個語句“Program SizeCode=xx RO-data=xx RWdata=xx ZI-data=xx”,它說明了程式各個域的大小,編譯後,應用程式中所有具有同一性質的資料(包括程式碼)

被歸到一個域,程式在儲存或執行的時候,不同的域會呈現不同的狀態,這些域的意義如下:

Code:即程式碼域,它指的是編譯器生成的機器指令,這些內容被儲存到 ROM 區。

RO-dataRead Only data,即只讀資料域,它指程式中用到的只讀資料,這些資料被儲存在 ROM 區,因而程式不能修改其內容。例如 C 語言中 const 關鍵字定義的變數就是典型的 RO-data

RW-dataRead Write data,即可讀寫資料域,它指初始化為“非 0 值”的可讀寫資料,程式剛執行時,這些資料具有非 0 的初始值,且執行的時候它們會常駐在RAM 區,因而應用程式可以修改其內容。例如 C

語言中使用定義的全域性變數,且定義時賦予“非 0 值”給該變數進行初始化

ZI-data: Zero Initialie data,即 0 初始化資料,它指初始化為“0 值”的可讀寫資料域,它與 RW-data 的區別是程式剛執行時這些資料初始值全都為 0,而後續執行過程與 RW-data 的性質一樣,它們也常駐在 RAM 區,因而應用程式可以更改其內容。例如 C 語言中使用定義的全域性變數,且定義時賦予“0 值”給該變數進行初始化(若定義該變數時沒有賦予初始值,編譯器會把它當 ZI-data 來對待,初始化為 0);

ZI-data 的棧空間(Stack)及堆空間(Heap):在 C 語言中,函式內部定義的區域性變數屬於棧空間,進入函式的時候從向棧空間申請記憶體給區域性變數,退出時釋放區域性變數,歸還記憶體空間。而使用 malloc 動態分配的變數屬於堆空間。在程式中的棧空間和堆空間都是屬於 ZI-data 區域的,這些空間都會被初始值化為 0 值。編譯器給出的 ZI-data 佔用的空間值中包含了堆疊的大小(經實際測試,若程式中完全沒有使用 malloc 動態申請堆空間,編譯器會優化,不把堆空間計算在內)

  程式元件所屬的區域

機器程式碼指令Code
常量RO-data
初值非0的全域性變數RW-datra
初值為0的全域性變數ZI-data
區域性變數ZI-data棧空間
使用malloc動態分配的空間ZI-data堆空間

  4 程式的儲存與執行

  RW-data ZI-data 它們僅僅是初始值不一樣而已,為什麼編譯器非要把它們區分開?這就涉及到程式的儲存狀態了,應用程式具有靜止狀態和執行狀態。靜止態的程式被儲存在非易失儲存器中,如 STM32 的內部 FLASH,因而系統掉電後也能正常儲存。但是當程式在執行狀態的時候,程式常常需要修改一些暫存資料,由於執行速度的要求,這些資料往往存放在記憶體中(RAM),掉電後這些資料會丟失。因此,程式在靜止與執行的時候它在儲存器中的表現是不一樣的。


  圖中的左側是應用程式的儲存狀態,右側是執行狀態,而上方是 RAM 儲存器區域,下方是 ROM 儲存器區域。程式在儲存狀態時, RO 節(RO section)及 RW 節都被儲存在 ROM 區。當程式開始執行時,核心直接從 ROM 中讀取程式碼,並且在執行主體程式碼前,會先執行一段載入程式碼,它把 RW 節資料從 ROM 複製到 RAM, 並且在 RAM 加入 ZI 節, ZI 節的資料都被初始化為
0。載入完後 RAM 區準備完畢,正式開始執行主體程式。編譯生成的 RW-data 的資料屬於圖中的 RW 節, ZI-data 的資料屬於圖中的 ZI 節。是否需要掉電儲存,這就是把 RW-data 與 ZI-data 區別開來的原因,因為在 RAM 建立資料的時候,預設值為 0,但如果有的資料要求初值非 0,那就需要使用 ROM 記錄該初始值,執行時再複製到 RAM。
  STM32 的 RO 區域不需要載入到 SRAM,核心直接從 FLASH 讀取指令執行。計算機系統的應用程式執行過程很類似,不過計算機系統的程式在儲存狀態時位於硬碟,執行的時候甚至會把上述的 RO 區域(程式碼、只讀資料)載入到記憶體,加快執行速度,還有虛擬記憶體管理單元(MMU)輔助載入資料,使得可以執行比實體記憶體還大的應用程式。而 STM32 沒有 MMU,所以無法支援 Linux 和 Windows 系統。
  當程式儲存到 STM32 晶片的內部 FLASH 時(即 ROM 區),它佔用的空間是 Code、RO-data 及 RW-data 的總和,所以如果這些內容比 STM32 晶片的 FLASH 空間大,程式就無法被正常儲存了。當程式在執行的時候,需要佔用內部 SRAM 空間(即 RAM 區),佔用的空間包括 RW-data 和 ZI-data。應用程式在各個狀態時各區域的組成見表 。


  MDK 中,我們建立的工程一般會選擇晶片型號,選擇後就有確定的 FLASH SRAM 大小,若程式碼超出了晶片的儲存器的極限,編譯器會提示錯誤,這時就需要裁剪程式了,裁剪時可針對超出的區域來優化。

5 後記

  STM32F1系列因為沒有MMU,所以不能跑作業系統。但是比如三星的S3C64310,在其資料手冊上可以看到這麼一句話:It also includes a full MMU to handle virtual memory management. 這說明S3C6410是具備MMU的,可以管理虛擬記憶體。



相關推薦

MDK程式設計過程變數儲存位置

1 參考書籍《零死角玩轉STM32-F429》2 程式設計過程  首先我們簡單瞭解下 MDK 的編譯過程,它與其它編譯器的工作過程是類似的,該過程見圖 51-1。  (1) 編譯, MDK 軟體使用的編

C++ 程式碼變數儲存位置

棧:程式自動分配,編譯器在需要的時候分配,不需要的時候自動清除的變數儲存區 堆:程式設計師手動分配由new分配的記憶體,需要自己在程式裡面手動釋放 常量儲存區:存放的是常量,不允許修改,編譯時分配,程

c語言==變數儲存位置堆疊的區別(18)

明顯的看出全域性變數還有static是儲存在一起的,區域性變數在一起,常數有自己的地方,malloc也是。 從而可以知道虛擬地址空間的分配: 棧空間存放區域性變數,函式形參 堆空間存放malloc,relloc,calloc分配空間、 資料段裡面有B

Java記憶體分配及變數儲存位置例項講解

Java記憶體分配與管理是Java的核心技術之一,之前我們曾介紹過Java的記憶體管理與記憶體洩露以及Java垃圾回收方面的知識,今天我們再次深入Java核心,詳細介紹一下Java在記憶體分配方面的知識。一般Java在記憶體分配時會涉及到以下區域:  ◆暫存器:我們在程式中無

JVM 變數儲存位置

1.暫存器:最快的儲存區, 由編譯器根據需求進行分配,我們在程式中無法控制.2. 棧:方法執行時建立方法棧幀,存放基本型別的變數資料和物件的引用,但物件本身不存放在棧中,而是存放在堆(new 出來的物件)或者常量池中(字串常量物件存放在常量池中。)3. 堆:存放所有new出來

java 中變數儲存位置的區別

1.暫存器:最快的儲存區, 由編譯器根據需求進行分配,我們在程式中無法控制.  2. 棧:存放基本型別的變數資料和物件的引用,但物件本身不存放在棧中,而是存放在堆(new 出來的物件)或者常量池中(字串常量物件存放在常量池中。)  3. 堆:存放所有new出來的物件。 

常量字串,字串變數——儲存位置

注意: 指標方式建立的字元陣列,是常量字串,指標指向的內容是沒法更改的;方括號([])方式建立的字元陣列僅僅是變數,內容可以更改。    string 物件沒有  ‘\0’         char text[]  字串陣列若沒有 '\0',只能當做陣列處理,若有 '

除錯oracle儲存過程sqlserver儲存過程

除錯oracle儲存過程 環境: win2003 server + oracle9i + pl/sql developer 7 除錯方法: 1 在pl/sql developer 裡右擊要除錯的儲存過程,選擇"新增除錯資訊"命令 2 如果儲存過程沒有語法錯誤,右擊儲存過程,選擇"測試"命令 3 在"

JVM變數值的儲存位置

今天看java程式設計思想的時候看到了“引用”這個詞,忽然對java中變數和值的儲存結構有點迷糊,然後百度找了好久才找到,還是記錄一下好了,加深一下印象。 1.JVM的儲存結構 暫存器:最快的儲存區,位於處理器內部,由編譯器根據需求進行分配,我們在程式中無法控制 棧:位

各種變數、字串(儲存位置生命週期)

// 轉載至https://blog.csdn.net/mcu_tian/article/details/37910835 常量儲存總結 區域性變數、靜態區域性變數、全域性變數、全域性靜態變數、字串常量以及動態申請的記憶體區          

C語言變數的型別儲存位置

1. C語言變數主要分為全域性變數、靜態全域性變數、區域性變數、靜態區域性變數和暫存器變數。其中靜態變數用static關鍵字進行修飾。程式所佔用的記憶體可以分為以下幾個部分: (1)程式碼段-存放程式程式碼,只讀的,不能修改。 (2)全域性區(靜態區),又稱為資料段。其中.data段存放的是

C語言程式碼各種常量、變數在記憶體中的儲存位置及記憶體優化

全域性變數、靜態區域性變數儲存在全域性資料區,初始化的和未初始化的分別儲存在一起; 普通區域性變數儲存在堆疊中; 全域性變數和區域性變數在記憶體裡的區別? 一、預備知識—程式的記憶體分配  一個由c/C++編譯的程式佔用的記憶體分為以下幾個部分  1、棧區(stack

mysql儲存過程變數(建立(DECLARE)賦值(SET))

    我們都知道,變數是一個命名資料物件,變數的值可以在儲存過程執行期間更改。我們接下來就嘗試使用儲存過程中的變數來儲存直接/間接結果。 這些變數是儲存過程的本地變數,但是我們得注意,變數必須先聲明後

區域性變數全域性變數在記憶體中的儲存位置

靜態儲存方式:是指在程式執行期間分配固定的儲存空間的方式。 動態儲存方式:是在程式執行期間根據需要進行動態的分配儲存空間的方式。 使用者儲存空間可以分為三個部分: 1)程式區; 2)靜態儲存區; 3)動態儲存區; 全域性變數全部存放在靜態儲存區,在程式開始執行時給全域性

SQL Server中的CLR程式設計——用.NET為SQL Server編寫儲存過程函式

1、開啟VS2010 新建專案->類庫->AdditionalMD5 2、1.建立類(函式)方法->FunctionMD5    2.建立類(儲存過程)方法->GetMD5 3、然後在SQL SERVER 資料庫中操作 1.目標資料庫例項需要啟

變數宣告定義的區別||變數儲存類別小結(C程式設計中的內容)

我們在程式設計中,時時刻刻都用到變數的定義和變數的宣告,可有些時候我們對這個概念不是很清楚,知道它是怎麼用,但卻不知是怎麼一會事,下面我就簡單的把他們的區別介紹如下: 變數的宣告有兩種情況:

MySQL儲存過程函式的區別與優缺點

為什麼要使用儲存過程和函式 資料庫物件儲存過程和函式,是用來實現一組關於表操作的SQL語句程式碼當做一個整體來執行。一個完整的操作會包含多條SQL語句,在執行過程中需要根據前面的SQL語句來執行結果有選擇的執行後面的SQL語句。 儲存過程和函式可以簡單的理解為一條或多條SQL語句的集合

6.儲存過程函式-mysql

儲存過程和函式是在資料庫中定義一些SQL語句的集合。然後需要哪些功能的時候,直接掉用儲存過程和函式來執行已經定義好了的SQL語句,引入儲存過程和函式可以減少開發人員編寫重複的SQL。 儲存過程和函式是在MYSQL的伺服器中儲存執行的,這個SQL語句是已經編譯過了的,可以減少了客戶端服務端資料傳

keil mdk除錯過程中檢視區域性變數的方法

      上次除錯STM32做了一次總結,此次在除錯nordic 51822時發現區域性變數地址給不出任何資訊, 導致無法檢視區域性變數值。通過和STM32的設定進行必較發現C/C++的編譯器等級設定過高,而將 一些區域性變數優化掉而沒有分配記憶體地址。

u-boot-2014.10移植(7)修改環境變數儲存位置

原來環境變數儲存在nor flash裡, 前面mtdparts分割槽第二個分割槽就是params 現在修改環境變數到nand裡,  搜尋default environment 在Env_common.c函式裡面: default_environment結構體default_environme