您好,登錄后才能下訂單哦!
我們本節來看看程序的結構體系。我們在平時所編寫的程序中,其實它的結構是非常值得研究的,這樣有助于我們編寫出效率更高的代碼,并且也有助于我們理解整個可執行文件的架構。好了,廢話不多說。那么我們的程序究竟是由什么構成的呢?它是由不同的段所構成的,包括代碼段、數據段等。
程序有兩個特征,靜態特征和動態特征。靜態特征指的是指令和數據,而動態特征則指的是執行指令處理數據的動作。下來我們來看看程序源代碼到可執行程序文件的對應關系,如下圖所示
我們之前在 C 語言中曾經也說過這方面的知識,今天我們再次回顧下。由上圖我們可以看到初始化過的全局變量和初始化過的 static 修飾的局部變量都存儲在 .data 段,而沒有初始化的變量則存儲在 .bss 段,函數都存儲在 .text 段。
下來我們來一一介紹下上面的幾個段,首先是代碼段(.text),代碼段具有以下特征
1、源代碼中的可執行語句編譯后進入代碼段;
2、代碼段在有內存管理單元的系統中具有只讀屬性;
3、代碼段的大小在編譯結束后就已經固定(不能動態改變);
4、代碼段中可以包含常量數據(如常量字符串)。
其次是數據段,它包括(.data, .bss, .rodata)。數據段用于處處源代碼中具有全局生命期的變量,其中 .bss 段存儲未初始化(或初始化為 0)的變量、.data 段存儲具有非 0 的初始值的變量、.rodata 存儲 const 關鍵字修飾的變量。那么我們思考下:我們在前面說初始化過的和未初始化的全局變量和靜態局部變量是分開來存放的,為什么要搞的如此復雜呢?一般在程序加載后,.bss 段中的所有內存單元被初始化為 0,將程序文件中 .data 段相關的初始值寫入對應的內存單元。因為 .bss 段中的變量不用在程序文件中保存初始值,從而減少可執行程序文件的體積,并且提高程序的加載效率。
下來我們還是以代碼為例來進行分析說明
int g_no_var_v; int g_var_v = 1; int g_main() { static no_var_v1; static var_v2 = 2; return 0; }
我們編譯來看看結果
我們看到在 .data 和 .bss 段中各占了8字節。接下來我們把全局變量 g_no_var_v 的類型改為 char,然后再看看結果
我們看到結果還是 8,因為它是四字節對齊的,因此結果還是 8,如果我們改為兩個 char 類型的,那么結果便為 4 了。g_main() 的入口地址便是 .text 段的起始地址了,也再次證明了我們的程序是自己指定入口函數的了。
下來我們來看看棧(Stack)。棧在程序中的本質是一片連續存儲的內存空間,SP 寄存器作為棧頂“指針”實現入棧操作和出棧操作。如下圖所示
棧的作用大致有以下幾方面:
1、中斷發生時,棧用于保存寄存器的值;
2、函數調用時,棧用于保存函數的活動記錄(棧幀信息);
3、并發編程時,每一個線程擁有自己獨立的棧。
下來我們來看看堆(Heap),堆是一片“閑置”的內存空間,其目的是用于提供動態的內存分配;當然堆空間的分配是需要 malloc 函數的支持的,它在使用完成后也需要借助于 free 函數進行手動的釋放空間。再來看看內存映射段(Memory Mapping Segment),在內核中,是直接將硬盤文件的內容直接映射到內存映射段(mmap),動態鏈接庫在可執行程序加載時映射到內存映射段,以便程序在執行時能夠創建匿名映射區存放程序數據。
我們簡單來介紹下內存映射文件的原理:
1、將硬盤上的文件數據邏輯映射到內存中(零耗時);
2、通過缺頁中斷進行文件數據的實際載入(一次數據拷貝);
3、映射后的內存的讀寫就是對文件數據的讀寫;
映射關系如下:
在程序的結構體系中,它的整體分布如下圖所示
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。