程序結構分析和理解
1、目標文件的構成
二進制文件是由一系列的段構成的,當然也會存在一些符號,存儲器分配等等,從鏈接文本其實就能大概知道程序的組成,各個目標文件的同一段結合起來就實現了可執(zhí)行程序的鏈接,當然具體的鏈接方式和原則都是工具設計好的。比如鏈接的順序按著輸入的順序等。
記得在移植u-boot到TQ2440的過程中,曾經就修改過鏈接文本,也就是u-boot.lds文件,當時因為自己添加了一個關于nand flash 操作的函數,為了保證添加的函數不會在鏈接的過程中將啟動代碼之前而導致芯片啟動存在問題,我特意修改了程序的鏈接順序,保證鏈接文檔正確鏈接。
1. [root@Gong-Computer u-boot-2010.06]# vi arch/arm/cpu/arm920t/u-boot.lds
2. . = ALIGN(4);
3. .text :
4. {
5. arch/arm/cpu/arm920t/start.o (.text)
6. board/samsung/smdk2440/lowlevel_init.o (.text)
7. board/samsung/smdk2440/nand_read.o (.text)
8. *(.text)
9. }
關于二進制文件中的基本內容主要是包括幾個段的。當然還需要其他的一些條件,在鏈接的過程中,好像還需要一些C語言運行的環(huán)境,主要是用來控制程序的啟動和關閉。這些都是crt*(C RunTime)目標文件實現的。這些目標文件在我們程序設計中不經??吹?,如果分析過u-boot的編譯過程就會發(fā)現,這些目標文件確實存在。
鏈接文本使得文件的鏈接更加的方便和實用。因此了解目標的最基本段落是非常的必要的。
在其中需要注意的幾點:
1、靜態(tài)變量不管是局部還是全局的都是在數據段中,而不想局部變量在堆棧中,當棧彈出以后,其中的內容也就釋放了,靜態(tài)變量不會改變,但是對于局部靜態(tài)變量則只能被定義該變量的函數訪問,不能被其他的函數訪問。
2、全局變量、靜態(tài)變量如果被初始化為0,或者沒有被初始化,則該變量被分配到.bss(未初始化部分),只有當全局變量和靜態(tài)變量初始化為非零數值時才會分配到.data段中。
3、全局變量和靜態(tài)變量如果沒有被初始化一般都會默認為0,這也是為什么將這兩個數初始化為0,仍然處在.bss段的原因。因此需要注意全局變量和靜態(tài)變量在沒有初始化時的值,但是對于局部變量則沒有這個特點,如果沒有初始化則會出現一個隨機值。
以上的結論可以通過代碼進行手動測試。
2、Linux中程序結構
在Linux中每一個進程都存在一個4G的虛擬空間,其中前3G空間是用戶空間,而后1G空間則是內核空間,這4G的空間在各個進程之間都是相互獨立的。但是這些內存空間的區(qū)域分配確實相同的,而且各個區(qū)域的起始地址也是固定好的,Linux程序的結構如下:
由于各個段的起始地址都是固定的,這樣就便于虛擬地址到物理地址的映射,方便了程序的加載。特別是共享庫的實現。
3、堆棧和堆
其實我對這兩個概念在剛開始的時候也存在很大的誤解,不明白其中的關系,總是把堆棧理解成堆和棧,實質上堆棧就是指棧,搞清楚這個,兩者的區(qū)別就容易理解啦。堆棧其實也可以認為是一種數據結構,典型的先進后出特點。
一般而言堆棧都是反向增長的,也就是所謂的從高地址到低地址的增長方式,但是也有其他的增長方式,比如ARM有4種不同的增長模式,所以堆棧的增長方向只能依據CPU而言,不同的CPU其堆??赡艽嬖谝欢ǖ牟顒e。堆棧主要用來實現函數的調用。
堆一般而言都是在bass段的上面,主要用來實現動態(tài)內存的分配和釋放,在C語言中主要是通過malloc/free函數實現,在C++中則主要采用new/delete實現。但是對于這一塊的內存通常會不斷的分配和釋放以及滿足基本的對齊形式,這樣就導致了內存碎片的產生,減小了訪問的速率。同時因為在這段區(qū)域運行零空間申請,以及釋放后指針仍然有效等問題,所以在釋放完畢以后通常將指針指向NULL。
評論