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