Linux 內存管理概要

之前學習內存方面的都太關注於細節,就有些知其然而不知其所以然的感覺。所以這篇文章拋開細節實現,將Linux內存管理的大概思路整理一下。

Linux操作系統支持多任務系統,即(看上去)支持多任務併發處理。實際上,系統同時運行的進程數不會超過CPU數目,因此內核會在很短的時間間隔在不同的進程之間切換(用戶是注意不到),從而產生同時處理多進程的假象。

Linux對於每一個任務有分配一個虛擬地址空間,而CPU的字長決定了虛擬地址空間的大小,如32位CPU即2^{32}字節=4GB。

那麼,現在問題就有來了,給每個任務分配一個這麼大的虛擬地址空間是否實際內存會不夠用,虛擬地址空間又如何跟實際內存關聯起來的?

在解釋這個問題前,先釐清下一些特殊的情況。

Linux又將虛擬地址空間劃分爲兩部分,分別是內核空間用戶空間。如下圖所示,0~TASK_SIZE爲用戶空間,TASK_SIZE~2^{32}/2^{64}爲內核空間。每個任務是有自己的用戶空間,但每個任務的內核空間是相同的。

那麼,又有問題了。內核空間既然對每個任務是相同的,那麼豈不是違反了設計虛擬地址空間的初衷(即每個任務跑在自己地址空間,相互不打擾)?

每個用戶態任務是無法直接訪問內核空間內容的,而是通過系統調用間接訪問。大致flow是這樣的,用戶態任務發起系統調用後,會引發exception,對於ARM芯片架構就是由USER mode->IRQ mode->SVC mode,從而進入內核態,內核態的接口將內核空間的數據copy至用戶態進程的用戶空間,最後再恢復狀態,從而讓用戶態任務能夠拿到想要的數據。

這裏還介紹一個內核態任務。與用戶態任務不同,內核態任務只會訪問內核空間,且所有內核態任務共用內核空間。

在回到之前的問題,給每個任務分配一個這麼大的虛擬地址空間是否實際內存會不夠用,虛擬地址空間又如何跟實際內存關聯起來的?

那麼這邊要介紹三個概念,頁,頁表,頁幀

每個虛擬地址空間都會被內核分爲等長的部分,這每個部分,被稱爲。而實際內存也被分爲等長的部分,被稱爲頁幀。下圖爲兩個虛擬地址空間與實際內存的映射圖。而用於保存該映射關係的,被稱爲頁表

一個進程是用多少頁,纔會對應申請多少頁幀。所以實際內存不會因虛擬地址爲2^{32}/2^{64}而佔用相同的實際內存。

現在算一筆賬,一頁一般是4KB,以2^{32}即4GB虛擬內存爲例,那麼便是需要2^{20}個頁表項,而地址是32位的,即每個地址4Byte去保存,那麼一個進程的頁表需要4MB的空間去存儲。那麼50個進程就需要200MB的空間,這無疑是非常浪費的。

好吧,現在看這個讀者肯定有各種思路的要反駁我,不着急,我們先介紹一下,以已說明的這種方式是如何找到實際內存的。

首先,用戶態進程有保存頁表項首地址(實際地址)。進程想訪問虛擬地址011..011(32位),那麼會將虛擬地址除以4K(2^{12})即該地址的前20位,該結果指示是第幾個頁表項,通過頁表項的首地址找到對應頁表,從而找到實際地址位置,然後再根據虛擬地址的後12位,得到對應偏移。這其實就要求,保存頁表項的實際內存地址一定是連續的,如果不連續,難道還要再建一個表去保存對應頁表項的實際地址,得不償失。

那麼建立頁這個概念,將內存分爲等大的頁的 意義也呼之欲出。保存內存爲4GB的頁表項內存都需要4M,那麼每一個字節都有自己映射,所需的表項內存爲2^{32}*4即16GB。這樣,還不如一一映射來的好一些。

那麼,再回到上面頁表項佔據一定內存,內存浪費問題。是否可以將頁的大小變大一些。其實從上面的介紹,可以看出,頁的大小是申請實際內存的最小單位,如果將頁的大小調的過大,那麼在實際使用中,會造成申請一頁中,只使用了其中一部分,造成更多浪費,術語是內碎片

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章