天天看點

深入了解程式的結構深入了解程式的結構

程式由不同的段構成(代碼段、資料段),程式的靜态特征就是指令和資料,動态特征就是執行指令處理資料。

源程式代碼到可執行程式的對應關系:

深入了解程式的結構深入了解程式的結構

源代碼的可執行語句編譯後進入代碼段,編譯完成後大小固定

代碼段在記憶體管理單元的系統中具有隻讀屬性(是一種保護,防止被改寫)

代碼段中可以包括常量資料(如字元串常量)

.bss段(block started by symbol segment)

存儲未初始化的全局資料,不占用可執行檔案的大小。

.data段:存儲具有非0初始值的變量

.rodata段:存儲const修飾的和其他隻讀資料

問題:.bss和.data段同樣存儲的是全局資料,為什麼初始化的和不初始化的儲存在不同的段中?

.bss段中的變量不用在再程式檔案中儲存初始值,進而減小可執程式的大小,提高程式加載的效率。(對于.bss段中的變量,在可執行檔案中隻需要儲存其變量名和變量類型,在加載時統一将其初始化為0;而對于存儲于.data段中的資料,需要儲存其變量名,類型、和值,在加載其需要将變量值拷貝得到變量對應的空間)

程式設計實驗:可以編寫簡單測試程式,通過objdump -h指令檢視各個段的資訊,使用nm指令檢視可執行檔案中的符号和位址,使用objdump -s -j .data ./a.ou檢視某個段中的具體資訊,并将上述資訊對應起來。

棧時在程式被加載到記憶體之後才生成的,其本質時片連續的存儲空間

sp寄存器作為棧頂“指針”實作入棧操作和出棧操作

深入了解程式的結構深入了解程式的結構

棧通常作為以下用途

中斷發生時,棧用于儲存寄存器的值(現場保護)

函數調用時,棧用于儲存函數的活動記錄(棧幀資訊)

并發程式設計時,每個線程擁有自己獨立的棧

堆和棧一樣,時程式被加載到記憶體後才生成的。是一片“空閑的空間”,用于提供動态記憶體配置設定。

需要函數的支援(malloc、free)

記憶體映射段(memory mapping segment)

核心将硬碟中的檔案内容直接映射到記憶體映射段(mmap)

動态連結庫在可執行程式加載時映射到記憶體映射段

程式執行時能夠建立匿名映射區存放程式資料

記憶體映射檔案的原理:

将硬碟上的檔案資料邏輯映射到記憶體中(零耗時),通過缺頁中斷進行檔案資料的實際載入(一次資料拷貝),映射後對記憶體的讀寫就是對檔案的讀寫(極大的提高了檔案的讀寫效率)。

深入了解程式的結構深入了解程式的結構

使用傳統的方式通過read函數來讀取檔案,首先核心程式接到應用程的請求,然後核心程式去讀取硬碟中的檔案核心空間,然後再講核心空間中的資料拷貝到應用空間(使用了兩次資料拷貝)。

最終各個段在記憶體中的布局:

深入了解程式的結構深入了解程式的結構

這裡我們看到棧、堆的起始位址都是随機的,這樣做的目的是為了安全,當其實位址随機後,惡意代碼修改程式的暗度變大(難以直接通過固定位址擷取到程度的傳回位址)。