Linux 可執行檔結構與進程結構,linux可執行檔
Linux可執行檔結構
在 Linux 下,程式是一個普通的可執行檔,以下列出一個二進位可執行檔的基本情況:
可以看出,此可執行檔在儲存時(沒有調入到內容)分為代碼區(text)、資料區(data)和未初始化資料區(bss)3 個部分。各段基本內容說明如下:
代碼區:
存放 CPU 執行的機器指令。通常代碼區是可共用的(即另外的執行程式可以調用它),使其可共用的目的是對於頻繁被執行的程式,只需要在記憶體中有一份代碼即可。代碼區通常是唯讀,使其唯讀原因是防止程式意外地修改了它的指令。另外,代碼區還規划了局部變數的相關資訊。
代碼區的指令包括作業碼和操作對象(或對象地址引用)。如果是立即數(即是具體的數值),將直接包含在代碼中,如果是局部資料,將在運行時在棧區分配空間,然後再引用該資料的地址,如果是未初始化資料區和資料區,在代碼中同樣將引用該資料的地址。
全域初始化資料區/待用資料區(資料區段):
該區包含了在程式中明確被初始化的全域變數、已經初始化的靜態變數(包括全域靜態變數和局部靜態變數)和常量資料(如字串常量)。
例如,一個不在任何函數內聲明(全域變數),如下:
int count = 100;
使得變數 count 根據其初始值被儲存初始化資料區中。
在任意位置定義靜態變數方式如下:
static int num = 200;
這聲明了一個待用資料並初始化,如果在任意函數體外聲明,則表示其為一個靜態全域變數,如果在函數體內(局部),則表示其為一個局部靜態變數。另外,如果在一個函數名前加上 static,則表示此函數只能再當前檔案中被調用。
未初始化資料區(又叫 BSS 區):
存入的是全域未初始設定變數和未初始化靜態變數。未初始化資料區的資料在程式開始執行之前被核心初始化為 0 或者空(NULL)。
例如,一個不在任何函數內聲明的未初始設定變數。
long sum[1000];
將 sum 儲存到未初始化資料區。
Linux進程結構
在 Linux 系統下,如果將某個可執行檔載入到記憶體運行,則將演變成一個或多個進程(多個進程的原因是進程在運行時可以再建立新的進程,但載入時只有一個進程)。進程是 Linux 交易管理的基本單元,所有的進程均擁有自己獨立的處理環境和系統資源。進程的環境由當前系統狀態及其父進程資訊決定和組成的。
為可執行檔儲存結構和 Linux 進程基本結構(部分)的對照圖。
一個進程是一個運行著的程式段,一個進程主要包括在記憶體中申請的空間,代碼(載入的程式,包括程式碼片段,資料區段,BSS),堆,棧,以及核心提供的核心進程資訊結構體
task_struct (位置在 /usr/include/linux/sched.h)、開啟的檔案、上下文(指進程執行活動全過程的靜態描述)資訊以及掛起的訊號等。
(1)代碼區(text segment)。載入的是可執行檔程式碼片段,其載入到記憶體中的位置由載入器完成。
(2)全域初始化資料區/待用資料區(Data Segment)。載入的是可執行檔資料區段,儲存於資料區段(全域初始化,靜態初始化資料)的資料的生存周期為整個程式運行過程。
(3)未初始化資料區(BSS)。載入的是可執行檔BSS段,位置可以分開亦可以緊靠資料區段,儲存於資料區段的資料(全域未初始化,靜態未初始化資料)的生存周期為整個程式運行過程。
(4)棧區(stack)。由編譯器自動分配釋放,存放函數的參數值、返回值、局部變數等。在程式運行過程中即時載入和釋放,因此,局部變數的生存周期為申請到釋放該段棧空間。
(5)堆區(heap)。用於動態記憶體分配。堆在記憶體中位於BSS區和棧區之間。一般由程式員分配和釋放,若程式員不釋放,程式結束時有可能由OS回收。
系統之所以分成這麼多個地區,主要基於以下考慮:
- 程式碼片段和資料區段分開,運行時便於分開載入,在哈佛體繫結構的處理器將取得更好得流水線效率。
- 代碼時依次執行的,是由處理器 PC 指標依次讀入,而且代碼可以被多個程式共用,資料在整個運行過程中有可能多次被調用,如果將代碼和資料混合在一起將造成空間的浪費。
- 臨時資料以及需要再次使用的代碼在運行時放入棧中,生命週期短,便於提高資源使用率。
- 堆區可以由程式員分配和釋放,以便使用者自由分配,提高程式的靈活性。
C 各儲存類型比較
參考資料:《Linux進階程式設計》