前言
在最近的一個項目中,突然想起以前學習嵌入式系統啓動流程,所以藉此機會複習一下從燒錄程序到設備加載代碼運行的過程,加深印象。
一.程序的結構
一個程序一般分爲3段:text段,data段,bss段
text段:就是放程序代碼的,編譯時確定,只讀,
data段:存放在編譯階段(而非運行時)就能確定的數據,可讀可寫
就是通常所說的靜態存儲區,賦了初值的全局變量和靜態變量存放在這個區域,常量也存放在這個區域
bss段:定義而沒有賦初值的全局變量和靜態變量,放在這個區域
我們編譯完成後生成燒錄文件,一般單片機的HEX文件,還有些其他類型的文件,內容基本上都是上述三個段。接下來用燒錄工具把生成的文件燒錄到機器中的ROM或者FLASH。
二.設備啓動
以一般的單片機或者嵌入式Linux產品爲例,CPU芯片中一般還有兩個片內的ROM和RAM,ROM中存着一段芯片廠家出廠就寫好的指令。機器啓動時,CPU會運行ROM中的指令,把Flash中bootloader的第一部分,加載到CPU的RAM中,這個階段做的事是:
對於一般的單片機,可能沒有那麼複雜,簡單的來說,這兩個階段就是,初始化外部的RAM,初始化其他的硬件設備,把FLash中的text, data,bss三段加載到RAM中,加載的過程如下:
(1)爲全局變量分配地址空間---如果全局變量已賦初值,則將初始值從ROM中拷貝到RAM中,如果沒有賦初值,則這個全局變量所對應的地址下的初值爲0或者是不確定的。當然,如果已經指定了變量的地址空間,則直接定位到對應的地址就行,那麼這裏分配地址及定位地址的任務由“連接器”完成。
(2)設置堆棧段的長度及地址---用C語言開發的單片機程序裏面,普遍都沒有涉及到堆棧段長度的設置,但這不意味着不用設置。堆棧段主要是用來在中斷處理時起“保存現場”及“現場還原”的作用,其重要性不言而喻。而這麼重要的內容,也包含在了編譯器預設的內容裏面,確實省事,可並不一定省心。
(3)分配數據段data,常量段const,代碼段code的起始地址——代碼段與常量段的地址可以不管,它們都是固定在ROM裏面的,無論它們怎麼排列,都不會對程序產生影響。但是數據段的地址就必須得關心。數據段的數據時要從ROM拷貝到RAM中去的,而在RAM中,既有數據段data,也有堆棧段stack,還有通用的工作寄存器組。通常,工作寄存器組的地址是固定的,這就要求在絕對定址數據段時,不能使數據段覆蓋所有的工作寄存器組的地址。必須引起嚴重關注。
注:這裏所說的“第一行代碼處”,並不一定是你自己寫的程序代碼,絕大部分都是編譯器代勞的,或者是編譯器自帶的demo程序文件。因爲,你自己寫的程序(C語言程序)裏面,並不包含這些內容。高級一點的單片機,這些內容,都是在startup的文件裏面。
4、普通的flashMCU是在上電時或復位時,PC指針裏面的存放的是“0000”,表示CPU從ROM的0000地址開始執行指令,在該地址處放一條跳轉指令,使程序跳轉到_main函數中,然後根據不同的指令,一條一條的執行,當中斷髮生時(中斷數量也很有限,2~5箇中斷),按照系統分配的中斷向量表地址,在中斷向量裏面,放置一條跳轉到中斷服務程序的指令,如此如此,整個程序就跑起來了。決定CPU這樣做,是這種ROM結構所造成的。
注:特別的,如下
1--I/O口寄存器:也是可以被改變的量,它被安排在一個特別的RAM地址,爲系統所訪問,而不能將其他變量定義在這些位置。
2--中斷向量表:中斷向量表是被固定在MCU內部的ROM地址中,不同的地址對應不同的中斷。每次中斷產生時,直接調用對應的中斷服務子程序,將程序的入口地址放在中斷向量表中。
總結有如下幾段:
1、棧區(stack)— 由編譯器自動分配釋放 ,存放函數的參數值,局部變量的值等。其操作方式類似於數據結構中的棧。
2、堆區(heap) — 一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收 。注意它與數據結構中的堆是兩回事,分配方式倒是類似於鏈表,呵呵。
3、全局區(靜態區)(static)—,全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態變量在一塊區域, 未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域。 - 程序結束後有系統釋放
4、文字常量區—常量字符串就是放在這裏的。 程序結束後由系統釋放
5、程序代碼區—存放函數體的二進制代碼。
如果哪位有緣人看到覺得有幫助,就隨手端個讚唄~