程序可執(zhí)行文件的結(jié)構(gòu)一個程序的可執(zhí)行文件在內(nèi)存中的結(jié)果,從大的角度可以分為兩個部分:只讀部分和可讀寫部分。只讀部分包括程序代碼(.text)和程序中的常量(.rodata)??勺x寫部分(也就是變量)大致可以分成下面幾個部分:
下面就各個分區(qū)具體解釋一下: data 和 bss 區(qū) 這兩個區(qū)經(jīng)常放在一起說,因?yàn)樗麄兌际怯脕泶鎯θ肿兞亢挽o態(tài)變量的,區(qū)別在于 data 區(qū)存放的是初始化過的, bss 區(qū)存放的是沒有初始化過的,例如: int val = 3;char string[] = 'Hello World'; 這兩個變量的值會一開始被存儲在 .text 中(因?yàn)橹凳菍懺诖a里面的),在程序啟動時會拷貝到 .data 去區(qū)中。 而不初始化的話,像下面這樣: static int i; 這個變量就會被放在 bss 區(qū)中。 答疑一 靜態(tài)變量和全局變量 這兩個概念都是很常見的概念,又經(jīng)常在一起使用,很容易造成混淆。
a.c #include b.c int a;int compute(void){ a = 0; return a;} 在 Link 過程中會產(chǎn)生重復(fù)定義錯誤,因?yàn)橛袃蓚€全局的 a 變量,Linker 不知道應(yīng)該使用哪一個。為了避免這種情況,就需要引入 static。
對于全局變量來說,為了避免上面提到的重復(fù)定義錯誤,我們可以在一個文件中使用 static,另一個不使用。這樣使用 static 的就會使用自己的 a 變量,而沒有用 static 的會使用全局的 a 變量。當(dāng)然,最好兩個都使用 static,避免更多可能的命名沖突。 注意:'靜態(tài)'這個中文翻譯實(shí)在是有些莫名其妙,給人的感覺像是不可改變的,而實(shí)際上 static 跟不可改變沒有關(guān)系,不可改變的變量使用 const 關(guān)鍵字修飾,注意不要混淆。 Bonus 部分 —— extern: extern 是 C 語言中另一個關(guān)鍵字,用來指示變量或函數(shù)的定義在別的文件中,使用 extern 可以在多個源文件中共享某個變量,例如這里的例子。 extern 跟 static 在含義上是“水火不容”的,一個表示不能在別的地方用,一個表示要去別的地方找。如果同時使用的話,有兩種情況,一種是先使用 static,后使用 extern ,即: static int m;extern int m; 這種情況,后面的 m 實(shí)際上就是前面的 m 。如果反過來: extern int m;static int m; 這種情況的行為是未定義的,編譯器也會給出警告。 答疑二 程序在內(nèi)存和硬盤上不同的存在形式 這里我們提到的幾個區(qū),是指程序在內(nèi)存中的存在形式。和程序在硬盤上存儲的格式不是完全對應(yīng)的。程序在硬盤上存儲的格式更加復(fù)雜,而且是和操作系統(tǒng)有關(guān)的,具體可以參考這里。一個比較明顯的例子可以幫你區(qū)分這個差別:之前我們提到過未定義的全局變量存儲在 棧棧是用于存放本地變量,內(nèi)部臨時變量以及有關(guān)上下文的內(nèi)存區(qū)域。程序在調(diào)用函數(shù)時,操作系統(tǒng)會自動通過壓棧和彈棧完成保存函數(shù)現(xiàn)場等操作,不需要程序員手動干預(yù)。 棧是一塊連續(xù)的內(nèi)存區(qū)域,棧頂?shù)牡刂泛蜅5淖畲笕萘渴窍到y(tǒng)預(yù)先規(guī)定好的。能從棧獲得的空間較小。如果申請的空間超過棧的剩余空間時,例如遞歸深度過深,將提示stackoverflow。 棧是機(jī)器系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu),計(jì)算機(jī)會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執(zhí)行,這就決定了棧的效率比較高。 堆堆是用于存放除了棧里的東西之外所有其他東西的內(nèi)存區(qū)域,當(dāng)使用 堆是向高地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu),是不連續(xù)的內(nèi)存區(qū)域。這是由于系統(tǒng)是用鏈表來存儲的空閑內(nèi)存地址的,自然是不連續(xù)的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限于計(jì)算機(jī)系統(tǒng)中有效的虛擬內(nèi)存。由此可見,堆獲得的空間比較靈活,也比較大。 對于堆來講,頻繁的new/delete勢必會造成內(nèi)存空間的不連續(xù),從而造成大量的碎片,使程序效率降低。對于棧來講,則不會存在這個問題,因?yàn)闂J窍冗M(jìn)后出的隊(duì)列,永遠(yuǎn)都不可能有一個內(nèi)存塊從棧中間彈出。 堆都是動態(tài)分配的,沒有靜態(tài)分配的堆。棧有2種分配方式:靜態(tài)分配和動態(tài)分配。靜態(tài)分配是編譯器完成的,比如局部變量的分配。動態(tài)分配由alloca函數(shù)進(jìn)行分配,但是棧的動態(tài)分配和堆是不同的,他的動態(tài)分配是由編譯器進(jìn)行釋放,無需我們手工實(shí)現(xiàn)。 計(jì)算機(jī)底層并沒有對堆的支持,堆則是C/C++函數(shù)庫提供的,同時由于上面提到的碎片問題,都會導(dǎo)致堆的效率比棧要低。 內(nèi)存分配
虛擬地址先經(jīng)過分段機(jī)制映射到線性地址,然后線性地址通過分頁機(jī)制映射到物理地址。 虛擬內(nèi)存
頁面置換算法
內(nèi)存抖動現(xiàn)象:頁面的頻繁更換,導(dǎo)致整個系統(tǒng)效率急劇下降,這個現(xiàn)象稱為內(nèi)存抖動(或顛簸)。抖動一般是內(nèi)存分配算法不好,內(nèi)存太小引或者程序的算法不佳引起的。 Belady現(xiàn)象:對有的頁面置換算法,頁錯誤率可能會隨著分配幀數(shù)增加而增加。 FIFO會產(chǎn)生Belady異常。 棧式算法無Belady異常,LRU,LFU(最不經(jīng)常使用),OPT都屬于棧式算法。 |
|
來自: 昵稱11935121 > 《未命名》