c語言程序的運行


13.4  C語言程序的運行

在嵌入式系統中,程序最終是要放置在內存中運行的,程序的幾個段,最終會轉化爲內存中的幾個區域。C語言可執行程序的內存佈局如圖13-5所示。

圖13-5  C語言可執行程序的內存佈局

在內存中,從低地址到高地址,依次是隻讀段、讀寫段、未初始化數據段、堆段、棧段。

映像文件中將包含代碼段(Code)、只讀數據段(RO Data)以及讀寫數據段(RW Data),未初始化數據段(BSS)將在程序的初始化階段中開闢,堆棧在程序運行時動態開闢。

只讀區(RO)包括了代碼和只讀數據,在內存區域中,代碼段(Code)和只讀數據段(Ro Data)的存放形式上基本沒有區別。

對於程序運行時的內存使用,堆和棧一般是相向擴展的。堆的分配由程序決定,棧由編譯器管理。

在以上概念中,只是一種內存分佈,並沒有考慮實際系統的情況。在實際的系統中,程序有載入和運行兩個概念。嵌入式系統由兩種內存,一種是可以固化只讀的內存(如:ROM,Nor Flash),另一種是易失的可讀寫的內存(如:SRAM和SDRAM)。程序中的各個段也有需要固化和需要讀寫的。程序中的各段必須載入到內存的恰當位置,程序纔可以運行。C語言各部分的需要固化和可寫的情況如表13-2所示。

表13-2  C語言各部分的需要支持固化和可寫的情況

需要固化

需要可寫

代碼(Code

只讀數據(RO data

讀寫數據(RW data

未初始化數據(BSS

堆(heap

棧(stack

在嵌入式系統中,經過編譯的C語言程序可以通過操作系統運行,也可以在沒有操作系統的情況下運行。程序存放的位置和運行的位置通常是不一樣的。

一般情況下,經過編譯後的程序存儲在Flash或者硬盤中,在運行時需要將程序加載到RAM中。嵌入式系統的Nor Flash和硬盤還有一定的差別,在硬盤的程序必須加載到RAM中才可以運行,但是在Nor Flash中的程序可以通過XIP(eXcutive In Place)的方式運行。

在嵌入式系統中,C語言程序的運行包括3種類型:第一種是調試階段的程序運行,這個階段程序存放的位置和運行的位置是相同的;第二種是程序直接在Flash中運行(XIP);第三種是將Flash或者硬盤中的程序完全加載到RAM中運行。

在C語言程序的運行中,存在着兩個基本的內存空間,一個是程序的存儲空間,另一個是程序的運行空間。程序的存儲空間必須包括代碼段、只讀數據段和讀寫數據段,程序的加載區域必須包括讀寫數據段和未初始化數據段如表13-3所示。

表13-3  C語言各部分使用的存儲空間

代碼

只讀數據

讀寫數據

未初始化數據

程序的存儲空間(ROM

需要

不需要

程序的加載空間(RAM

不需要

需要

由於程序放入系統後,必須包括所有需要的信息,代碼表示要運行的機器代碼,只讀數據和讀寫數據包含程序中預先設置好的數據值,這些都是需要固化存儲的,但是未初始化數據沒有初值,因此只需要標示它的大小,而不需要存儲區域。

在程序運行的初始化階段,將進行加載動作,其中讀寫數據和未初始化數據都是要在程序中進行“寫”操作,因此不可能放在只讀的區域內,必須放入RAM中。當然,程序也可以將代碼和只讀數據放入RAM。

在程序運行後,堆和棧將在程序運行過程中動態地分配和釋放。

13.4.1  RAM調試運行

本節介紹程序的一種特殊的運行方式,即在程序的調試階段將主機的映像文件直接放置到目標系統的RAM中。在這種應用中,RAM既是程序的存儲空間,也是程序的運行空間。

在嵌入式系統中,這是一種常用的調試方式,而不是通常的運行方式。在通常的運行方式下,程序運行的起始地址一 般不可能是RAM。RAM在掉電之後內容會丟失,因此係統上電的時候,RAM中一般不會有有效的程序。但是在程序的調試階段,可以將程序直接載入RAM, 然後在RAM的程序載入地址處運行程序。

嵌入式系統RAM中的調試程序的內存佈局如圖13-6所示。

圖13-6  RAM中的調試程序的內存佈局

這是一種相對簡單的形式,因爲代碼段的存儲地址和運行地址是相同的,都是RAM(SDRAM或者SRAM)中的地址。在這種情況下,程序沒有運行初始化階段加載的問題。

從主機向目標機載入程序的時候,程序映像文件中代碼段(code或text)、只讀數據段、讀寫數據段依次載入目標系統RAM(SDRAM或者SRAM)的空間中。

程序載入到目標機之後,將從代碼區的地址開始運行,在運行的初始化階段,將開闢未初始化數據區,並將其初始化爲0,在運行時將動態開闢堆區和棧區。

在沒有操作系統的情況下,開闢內存的工作都是由編譯器生成的代碼完成的,實現的原理是在映像文件中加入這些代 碼。主要工作包括:在程序運行時根據實際大小開闢未初始化的數據段;初始化棧區的指針,這個指針和物理內存的實際大小有關;在調用相關函數 (malloc、free)時使用堆區,這些函數一般由調用庫函數實現。表13-4列出了C語言程序在RAM中的調試過程。

表13-4  C語言程序在RAM中的調試過程

階段

涉及的部分

主要工作

程序的映像

代碼段(Code

只讀數據段(RO Data

讀寫數據段(RW Data

將程序放置在RAM

初始化階段

未初始化數據段(BSS

開闢BSS段並且清零

運行階段

代碼段(Code

只讀數據段(RO Data

讀寫數據段(RW Data

未初始化數據段(BSS

堆(heap

棧(stack

運行RAM代碼段中的程序,動態地在RAM中開闢堆和棧

知識點:程序直接載入RAM運行時,程序的加載位置和運行位置是一致的,因此不存在段複製的問題,需要在初始化階段開闢未初始化區域,在運行時使用堆棧。

13.4.2  固化程序的XIP運行

固化應用是一種嵌入式系統常用的運行方式,其前提是目標代碼位於目標系統ROM(Flash)中。ROM中的區域包括映像文件的代碼段(code或text)、只讀數據段(RO Data)、讀寫數據段(RW Data)。

以XIP(在位置執行)方式運行程序時內存佈局如圖13-7所示。

代碼的運行也是在ROM(Flash)中,因此,在編譯過程中代碼的存儲地址和運行地址是相同的,由於上電時需要啓動,因此該代碼的位置一般是(0x0)。

在這種應用中,一件重要的事情就是將已初始化讀寫段的數據從Flash中複製到SDRAM中,由於已初始化讀寫段既需要固化,也需要在運行時修改,因此這一步是必須有的,在程序的初始化階段需要完成這一步。

圖13-7  XIP運行程序時的內存佈局

一般來說,在編譯過程中需要定義讀寫段和未初始化段的地址。在程序中可獲取這些地址,然後就可以在程序的中加入複製的代碼,實現讀寫段的轉移。表13-5列出了C語言程序的XIP運行過程。

表13-5  C語言程序的XIP運行過程

    

涉及的部分

主要工作

程序的映像

代碼段(Code

只讀數據段(RO Data

讀寫數據段(RW Data

程序放置在Flash

初始化階段

讀寫數據段(RW Data

未初始化數據段(BSS

複製讀寫數據段到RAM

開闢未初始化段並且清零

運行階段

代碼段(Code

只讀數據段(RO Data

讀寫數據段(RW Data

未初始化數據段(BSS

堆(heap

棧(stack

運行Flash代碼段中的程序,動態地在RAM中開闢堆和棧

知識點:程序在ROM或者Flash中以XIP形式運行的時候,不需要複製代碼段和只讀數據段,但是需要在RAM中複製讀寫數據段,並另闢未初始化數據段。

13.4.3  固化程序的加載運行

在某些時候,在存放程序的位置是不能運行程序的,例如程序存儲在不能以XIP方式運行的Nand-Flash或者硬盤中,在這種情況下,必須將程序完全加載到RAM中才可以運行。固化程序加載運行的內存佈局如圖13-8所示:

 

圖13-8  固化程序加載運行的內存佈局

依照這種方式運行程序,需要將Flash中所有的內容全部複製到SDRAM或者SRAM中。在一般情況 下,SDRAM或者SRAM的速度要快於Flash。這樣做的另外一個好處是可以加快程序的運行速度。也就是說,即使Flash可以運行程序,將程序加載 到RAM中運行也還有一定的優勢。

這樣做也產生了另外一個問題:代碼段的載入地址和運行地址是不相同的,載入地址是在ROM(Flash)中,但是運行的地址是在RAM(SDRAM或者SRAM)中。對於這個問題,不同的系統在加載程序的時候有不同的解決方式。

C語言固化程序的加載運行過程如表13-6所示。

表13-6  C語言固化程序的加載運行時各段的情況

    

涉及的部分

主要工作

代碼的映像

代碼段(Code

只讀數據段(RO Data

讀寫數據段(RW Data

將程序放置在Flash

初始化階段

代碼段(Code

只讀數據段(RO Data

讀寫數據段(RW Data

未初始化數據段(BSS

加載代碼段和只讀數據段到RAM

複製讀寫數據段到RAM

開闢未初始化段並且清零

運行階段

代碼段(Code

只讀數據段(RO Data

讀寫數據段(RW Data

未初始化數據段(BSS

堆(heap

棧(stack

運行RAM代碼段中的程序,動態地在RAM中開闢堆和棧

知識點:固化程序在加載運行時,需要複製代碼段、只讀數據段和讀寫數據段到RAM中,並另闢未初始化數據段,然後在RAM中運行程序(執行代碼段)。

以這種加載方式的運行程序,另外一個重要的問題 是:如何把代碼移到RAM中。在有操作系統的情況下,代碼的複製工作是由操作系統完成的,在沒有操作系統的情況下,處理方式相對複雜,程序需要自我複製。 顯然,這種方式實現的前提是代碼最初放置在可以以XIP方式執行的內存中。

程序本身複製的過程也是需要通過程序代碼完成 的,這時需要程序中的代碼根據將包含自己的程序從ROM或者Flash中複製到RAM中。這是一個比較複雜的過程,程序的最前面部分是具有複製功能的代 碼。系統上電後,從ROM或者Flash起始地址運行,具有複製功能的代碼將全部代碼段和其他需要複製的部分複製到RAM中,然後跳轉到RAM中重新運行 程序。

固化程序加載複製和跳轉過程如圖13-9所示。

圖13-9  固化程序加載複製和跳轉過程

在代碼的前面一小部分是初始化的內容,這部分內容中有一部分是複製程序,這段複製程序將代碼段複製至RAM中,當這段初始化程序運行完成後,將跳轉到RAM中的某個地址運行。

13.4.4  C語言程序的運行總結

在上面幾節,主要介紹了C語言運行時內存的使用 情況。其關注點是程序中主要的段,事實上,程序可能不僅包括了上述主要段,還可能包括一些頭信息。程序實際的運行也分爲在操作系統下運行和直接運行等情 況。在具有操作系統的情況下,程序由操作系統加載運行,加載的時候可執行程序可以是一個文件,這個文件將包含程序的主要段以及頭信息。

對於Linux操作系統,目標程序是可執行的ELF(Executable and linking Format)格式;對於uCLinux操作系統,目標程序是Flat格式;對於需要在系統直接運行的程序,目標程序應該是純粹的二進制代碼,載入系統後,直接轉到代碼區地址執行。

事實上,無論運行環境如何,C語言程序在運行時所進行的動作都是類似的。程序在準備開始運行的時候,以下幾個條件都是必不可少的:

1.代碼段必須位於可運行的存儲區。

2.讀寫數據段必須在可以讀寫的內存中,而且必須經過初始化。

3.未初始化數據段必須在可以讀寫的內存中開闢,並被清空。

對於第1點,代碼段如果位於可以運行的存儲區域中(例如Nor Flash或者RAM),它就不需要加載,可以直接運行;如果代碼段位於不能運行的存儲區域中(例如:Nand Flash或者硬盤)中,它就必須被加載到RAM運行。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章