天天3分鐘操做系統修煉祕籍(8):虛擬內存分段

點我查看祕籍連載數組

進程的地址空間佈局:分段

Linux的虛擬地址空間採用「分段+分頁」結合的方式實現。先看分段,以後再介紹分頁。函數

分段是將內存劃分紅各個段落(Segment),每一個段落的長度能夠不一樣,且虛擬地址空間中未使用的空間不會映射到物理內存中,因此操做系統不會爲這段空間分配物理內存。這樣的話,內核爲剛建立的進程分配的物理內存能夠很小,隨着進程運行不斷使用內存,內核再爲進程按需分配物理內存。也就是說,儘管地址空間的範圍和物理內存大小同樣,但不會將所有空間映射到物理內存。佈局

對於Linux進程的虛擬地址空間來講,它的內存佈局以下圖。優化

虛擬空間分了以下幾個段:操作系統

  • 文本段(Text):也稱爲代碼段。進程啓動時會將程序的代碼加載到物理內存中,文本段映射到這片物理內存。
  • 初始化數據:包含程序顯式初始化的全局變量和靜態變量,這些數據是在程序真正運行前就已經肯定的數據,因此能夠提早加載到內存保存好。
  • 未初始化數據(BSS):未初始化的全局變量和靜態變量,這些變量的值是在程序真正運行起來併爲其賦值後才能肯定的,因此程序加載之初,只須要記錄它的內存地址和所需大小。出於歷史緣由,這段空間也稱爲BSS段。
  • 棧(Stack):是一個能夠動態增加和收縮的內存段落,由棧幀(Stack Frames)組成,進程每調用一次函數,都將爲該函數分配一個棧幀,棧幀中保存了該函數的局部變量、參數值和返回值。注意,編譯器會將函數參數放入寄存器來優化程序,只有寄存器放不下的參數才使用棧幀來保存。
  • 堆(Heap):程序運行時,變量的值以及動態請求分配的內存都在這個內存段落中。
  • 內核段(Kernel):這部分是操做系統內核運行時所佔用內存在各進程虛擬地址空間中的映射。全部進程都有,且映射地址相同,由於都映射到內核使用的內存。這段內存只有內核能訪問,用戶進程沒法訪問到該段落。

從上面的描述大概也能推測出,除了堆內存外,其它段落空間都是自動填充分配的,用戶沒法控制這些內存的使用。而堆內存段是用戶能使用的自由內存區,絕大多數程序的用戶數據都丟在這裏面,算是一個大雜燴空間。翻譯

例如,下圖中是一段C代碼和內存佈局之間的對應關係。設計

提示:其它語言的內存佈局
上面的佈局C程序的內存佈局,也是Linux下進程的內存佈局。其它語言(好比CPython)編寫的程序運行起來後,只要是在Linux下運行,其進程的佈局也會如此。只不過這些語言的程序中,全局變量、局部變量等可能和C的佈局不同,這和各語言的底層設計有關。好比C編寫的某動態語言,它不要求指定變量的數據類型,那麼在加載到內存的時候天然不知道該變量類型所需的空間大小,當它轉換成C後(儘管不會真的轉換成C代碼),這個變量只能丟進堆內存做爲動態數據。3d

使用分段的好處就是「各段自掃門前雪」,雖然在地址空間中每一個分段的地址都是連續的,但實際上,每一個分段映射到物理內存地址時是獨立的,段與段之間能夠不連續。這是由於CPU爲每一個段都使用一對(即兩個)特殊的寄存器:基址寄存器和界限寄存器blog

而界限寄存器中的值用來表示該段在物理內存中的大小,即已爲該段分配了多少內存。當準備用虛擬地址加基址計算物理地址時,須要先根據界限寄存器中的值檢查將要訪問的物理內存地址是否超出了這個段的範圍。若是超出了,則表示訪問了不屬於該段的內存,也即內存的越界訪問,而用戶進程是沒有權限訪問其它進程或未分配內存的地址的,這時會收到一個SIGSEGV(segmentation violation)信號並提示:Segmentation Fault,即段錯誤或段異常。收到這個信號後默認狀況下會終止該進程,由於它訪問了非法地址,可是能夠設置該信號的信號處理程序,從而作出其它處理。進程

例如,下圖中的進程訪問了Kernel段或者unallocated memory部分的內存,都會報錯。Kernel段除了內核進程,任何用戶進程都沒法訪問,典型的地址是用戶進程想要訪問0x0地址時,而該地址屬於Kernel,因此報錯。而unallocated memory是還未分配的內存,界限寄存器會保護該段沒法訪問。

再例如,C數組的越界訪問時也會出現該問題。下圖直觀地顯示了在Windows中一樣的內存越界錯誤。

也就是說,基址寄存器是用來轉換地址的,界限寄存器是用來保護進程不越界訪問內存的。CPU藉助基址寄存器和界限寄存器管理並提供地址翻譯和內存保護的功能,一般稱爲內存管理單元(Memory Management Unit,MMU)。

最後再說明一點,內存地址翻譯的任務既能夠由操做系統來作,也能夠由硬件CPU來作。但若是徹底由操做系統來完成,就須要頻繁地陷入到內核態,這樣效率會很是低。因此,這項任務交給CPU硬件來完成,操做系統只需在必要的時候介入,好比分配內存、回收內存等。

相關文章
相關標籤/搜索