進(jìn)程的虛擬內(nèi)存布局是怎樣的?
操作系統(tǒng)為了防止多進(jìn)程運(yùn)行時(shí)造成的內(nèi)存地址沖突,引入了虛擬內(nèi)存地址,為每個(gè)進(jìn)程提供了一個(gè)獨(dú)立的虛擬內(nèi)存空間,使得進(jìn)程以為自己獨(dú)占全部內(nèi)存資源。
在32位系統(tǒng)上,進(jìn)程擁有4GB虛擬內(nèi)存空間,在64位系統(tǒng)上,則可以擁有256T虛擬內(nèi)存空間。在進(jìn)程整個(gè)虛擬內(nèi)存空間中,又可以分為內(nèi)核空間和用戶空間兩部分。32 位系統(tǒng)的內(nèi)核空間占用 1G,位于最高處,剩下的 3G 是用戶空間。64 位系統(tǒng)只使用了低 48 位,內(nèi)核空間和用戶空間都是 128T,分別占據(jù)整個(gè)內(nèi)存空間的最高和最低處,剩下的中間部分是未定義的。
進(jìn)程在用戶態(tài)時(shí),只能訪問用戶空間內(nèi)存;只有進(jìn)入內(nèi)核態(tài)后,才可以訪問內(nèi)核空間內(nèi)存。雖然每個(gè)進(jìn)程的地址空間都包含了內(nèi)核空間,但這些內(nèi)核空間,其實(shí)關(guān)聯(lián)的都是相同的物理內(nèi)存。這樣,進(jìn)程切換到內(nèi)核態(tài)后,就可以很方便地訪問內(nèi)核空間內(nèi)存。
對(duì)于進(jìn)程虛擬內(nèi)存的用戶空間,從低往高,我們又可以分六個(gè)不同的內(nèi)存段。
1.代碼段
代碼段用來存放程序執(zhí)行代碼,也可能包含一些只讀的常量。這塊區(qū)域的大小在程序運(yùn)行時(shí)就已經(jīng)確定,并且為了防止代碼和常量遭到修改,代碼段被設(shè)置為只讀。
2.數(shù)據(jù)段
數(shù)據(jù)段用來存放程序中已初始化全局變量與靜態(tài)變量。
3.BSS 段
BSS段用來存放程序中未初始化的全局變量和靜態(tài)變量。
4.堆
堆是動(dòng)態(tài)內(nèi)存分配區(qū)域,用來存放動(dòng)態(tài)分配的內(nèi)存,堆內(nèi)存由用戶申請(qǐng)分配和釋放,從低地址向高地址增長。
5.文件映射段
文件映射段也叫共享區(qū),主要包括共享內(nèi)存、動(dòng)態(tài)鏈接庫等共享資源,從低地址向高地址增長。
6.棧
棧用來存放程序中臨時(shí)創(chuàng)建的局部變量,如函數(shù)的參數(shù)、內(nèi)部變量等。每當(dāng)一個(gè)函數(shù)被調(diào)用時(shí),就會(huì)將參數(shù)壓入進(jìn)程調(diào)用棧中,調(diào)用結(jié)束后返回值也會(huì)被放回棧中。同時(shí),每調(diào)用一次函數(shù)就會(huì)創(chuàng)建一個(gè)新的棧,所以在遞歸較深時(shí)容易導(dǎo)致棧溢出。棧內(nèi)存的申請(qǐng)和釋放由編譯器自動(dòng)完成,并且棧容量由系統(tǒng)預(yù)先定義。棧從高地址向低地址增長。
堆和文件映射段的內(nèi)存是動(dòng)態(tài)分配的。比如說,使用 C 標(biāo)準(zhǔn)庫的 malloc() 或者 mmap() ,就可以分別在堆和文件映射段動(dòng)態(tài)分配內(nèi)存。