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