1. 程式人生 > >一個程式在執行main函式之前都幹了些什麼?

一個程式在執行main函式之前都幹了些什麼?

《一 》怎麼執行程式(如何把程式載入到記憶體上

首先記憶體需要的是資料和指令(機器語言)但是程式是高階語言,

1:先通過編譯連結生成.exe檔案(.exe檔案在磁碟中儲存,且.exe檔案中是機器語言)

2:.exe檔案通過mmap函式對映到虛擬記憶體上

3:再通過分段分頁機制把需要的指令和資料載入到記憶體

4:把main函式的入口地址寫入到下一行指令暫存器中

《二》編譯連結的過程

預處理:

將所有的”#define”刪除,並且展開所有的巨集定義.

處理所有的條件編譯指令,比如:”#if”

處理”#include”預編譯指令,將包含的檔案插入到該預編譯指令的位置(拷貝一份),該過程是遞迴的可能會重複包含.

刪除所有的註釋

新增行號和檔名標識

保留所有的#pragma編譯器指令

編譯:生成.s檔案(彙編程式碼檔案)

詞法分析    語法分析     語義分析      程式碼優化

彙編:生成.o檔案,它是不可執行的.

將彙編程式碼轉換成機器可以執行的 指令.

連結:檔案格式 linux(elf)windows(pf)

合併段和符號表:(連結錯誤一般都發生在符號表中,它只關心全域性符號)相同的段進行合併

符號分析:在符號引用的地方找到符號定義的地方,處理外部引用的符號(進行替換) (把虛擬(*UND*)的資料和地址變為真實的資料和地址)

分配地址記憶體空間

符號的重定位(在指令段中發生)

《三》虛擬地址空間大小

大小:4G(32位作業系統)    使用者空間3G  核心空間1G

 堆和棧的區別:

堆:   1:手動開闢手動釋放     2:開闢的空間不連續    3:從低地址向高地址延伸      4:剩餘的大小就是堆的大小

棧:  1:系統開闢系統釋放      2:開闢的空間是連續的      3:從高地址向低地址延伸       4:大小不到1M  (用遞迴的方法驗證,如下圖)

#include<stdio.h>
int i=1;
void fun()
{
    char arr[1024];  //1k
    printf("%d  ",i);
    i++;
    fun();
}
void main()
{
    fun();
}

ELF 檔案一般包含 一下幾個程式碼段 :在linux中Readelf  -h main.o 檢視(elfheader)

file header:欄位裡存放了描述整個檔案的基本屬性資訊的內容,如程式入口地址,其他各段資訊(偏移量和範圍)

rodata欄位 :是存放只讀資料

common : 存放註釋的

data段:存放已初始化且初始化不為0的全域性變數的一塊記憶體區域。資料段屬於靜態記憶體分配。(初始化後的非const的全域性變數變數或者區域性static變數。)

bss段:存放未初始化的全域性變數的一塊記憶體區域且初始化為0的資料, BSS段屬於靜態記憶體分配(未初始化後的非const全域性變數和區域性static變數)

.text段:通常是指用來存放程式執行程式碼的一塊記憶體區域。這部分割槽域的大小在程式執行前就已經確定,並且記憶體區域通常屬於只讀, 某些架構也允許程式碼段為可寫,即允許修改程式。在程式碼段中,也有可能包含一些只讀的常數變數,例如字串常量等。(主要是編譯後的原始碼指令,是隻讀欄位。)

擴充套件 : 全域性的未初始化變數存在於.bss段中,具體體現為一個佔位符;全域性的已初始化變數存於.data段中;而函式內的自動變數都在棧上分配空間。.bss是不佔用.exe檔案空間的,其內容由作業系統初始化(清零);而.data卻需要佔用,其內容由程式初始化,因此造成了上述情況。

.bss段和.data段的區別:

bss段(未手動初始化的資料)並不給該段的資料分配空間,只是記錄資料所需空間的大小。
data(已手動初始化的資料)段則為資料分配空間,資料儲存在目標檔案中。 資料段包含經過初始化的全域性變數以及它們的值。BSS段的大小從可執行檔案中得到 ,然後連結器得到這個大小的記憶體塊,緊跟在資料段後面。當這個記憶體區進入程式的地址空間後全部清零。包含資料段和BSS段的整個區段此時通常稱為資料區。

區域性變數都是指令嗎?  區域性變數在什麼時候開闢空間? 

檔案中不存在.bss段 從elfheader中的sectiion headers中提取資訊   

在用的時候開闢空間  指令都存在在text段.用的時候開闢空間

Bss段到底節省了什麼空間 ?

BSS段節省了檔案空間

全域性變數分為強符號和弱符號?

強符號:已初始化的符號

弱符號:未初始化的符號

兩個強符號編譯報錯(重定義)   一強一弱取強符號的地址(弱符號暫時放在com塊中,連線後在.bss段)    兩個弱符號(和編譯器有關 1,報錯  2,就近原則)

外部引用的變數都放在哪裡?

外部引用的變數在編譯是放在*UND*中(因為編譯是檔案單獨編譯 此檔案中並不知道外部變數的地址所以暫時放在*UND*中)連線時會找到它的地址

外部引用的函式?