前面講了單純的操作系統的虛擬內存、linux進程的虛擬地址,然後又講了linux的進程的內存佈局,其實那張圖就是linux進程的虛擬內存。

取自:https://blog.csdn.net/dlutbrucezhang/article/details/9058583

可是感覺理解不夠,所以再來學習: 下面這部分是參考這個視頻學習的:

bilibili.com/video/av42 這個說的是linux的 swap

這稱為交換過程。這樣你的linux系統就可以釋放一些ram空間,並且由於缺乏內存不會崩潰。因為 linux swap是擴展ram的一種非常有用的方法,因為它提供了必要的ram空間耗盡且必須繼續進程時的額外內存。所以這就是為什麼你安裝linux的時候會提示你要為交換linux內核通常使用RAM內存來存儲臨時信息,當沒有足夠的ram空間時,linux內核會從中獲取一些ram信息,並將其寫入硬碟驅動器上的交換空間。分區分配空間。如下圖:

我們需要linux swap嗎?

通常你的ram已滿並且linux內核沒有可寫空間時,你的系統將崩潰。但如果你有交換分區,那麼linux內核和程序會使用它,所以你的程序不會崩潰,可以繼續工作但是速度會慢很多。因此,擁有交換空間更安全。

交換分區有一個缺點:它比ram慢很多,因此,添加交換空間不會使你的計算機運行速度更快,它只會幫助克服一些ram大小帶來的限制。

交換空間有兩個選項:交換分區和交換文件。

交換分區是硬碟驅動器的一部分,他是為交換空間保留的。

交換分區的大小:

1g ram----2g swap

2-4g ram ----2-4g swap

8g ram ---- 4g swap

>8g -----2-4g swap

(我的win10是4g內存,8g虛擬內存,阿里雲上的linux swapon沒有反應)

我看完後發現,這個和虛擬內存的思想很像很像,可是又沒有確定的解釋它和linux的虛擬內存的關係。看來瞭解的還是太少了。

然後來看下一個視頻:這個內容不錯

學習於:bilibili.com/video/av32

下面是核心圖:大家放大看會更好一些:

高級語言的程序代碼,經過編譯鏈接後,編程二進位的機器碼存儲在計算機的外存中。每個進程的代碼都有它的地址。這些機器指令序列是分頁存儲的,一般是4kb。就這樣一頁一頁地排在外存中。然後要運行的頁面就載入進內存中,不運行的就放在外存中。然後在每個進程的內核空間裏都存在著頁表,頁表裡存儲此進程的程序頁的編號,是否在內存,程序頁的在此進程中的虛擬地址,程序頁的物理內存地址,程序頁的外存地址。如果該程序頁在內存中,就沒有外存地址。

然後看cpu是怎麼執行指令的。

cpu運行一個進程的時候,要去取下一條指令的地址。這時候它收到的地址是進程傳過來的進程的虛擬地址,也就是說指令地址寄存器pc存放的是虛擬地址,然後經過MMU(memory manager unit)內存管理單元,MMU先通過該進程的頁表檢查指令所在頁是否在內存中,如果是,則直接去內存中去頁表裡的內存地址取出指令,然後執行。如果不在內存中,就去外存中找到該指令,把指令先載入到內存中,再將虛擬地址轉為內存地址,再從內存中取出指令執行,然後更新頁表。

這裡強調:虛擬內存是指針對cpu而言,cpu處理的是進程裏的虛擬地址,而不是操作具體的物理地址。也就是說cpu看到的是隻是虛擬地址,所以cpu認為這個虛擬地址對應這個內存,而這個內存是不存在的,所以我們認為這是虛擬內存。

(這位老師的說的頁面置換原則是隻要頁面沒運行,就置換出外存,而操作系統課上有很多的頁面置換演算法,比如LRU,FIFO之類的,這方面我不太清楚,但是這裡的思想感覺是沒錯的,也很細緻了)

至於缺頁中斷,tlb,局部性原理提高命中那方面的知識,前面的文章已經講過了,就不再這重複了。對了,這裡提醒一下,進程的內核空間應該是存儲在內存中的,而且各個進程的內核空間對應的都是內存中的同一個內核空間。裡面分別有各個進程的pcb,頁表等東西。

看完後我就產生了其它方面的疑問:置換出去的都是用戶空間的代碼段和數據段嗎?代碼段和數據段很大嗎?

下面是參考《深入理解計算機系統》:

虛擬內存的三個重要的能力:

1.它將主存看成是一個存儲在磁碟上的地址空間的高速緩存,在主存中只保存活動區域,並根據需要在磁碟和主存之間來回傳送數據,通過這種方式,它高效地使用了主存。

2.它為每個進程提供了一致的地址空間,從而簡化了內存管理。

3.它保護了每個進程的地址空間不被其它進程破壞。

後面看:騰訊雲技術社區:十問 Linux 虛擬內存管理 (glibc) (一) 又學到了一波東西。

這裡摘錄一些思考和筆記:

第一個理解:

進程使用多少內存可通過 ps aux 命令 查看,其中關鍵的兩信息(第五、六列)為:

  1. VSZ , virtual memory size ,表示進程總共使用的虛擬地址空間大小,包括進程地址空間的代碼段、數據段、堆、文件映射區域、棧、內核空間等所有虛擬地址使用的總和,單位是 K
  2. RSS , resident set size ,表示進程實際使用的物理內存空間, RSS 總小於 VSZ

然後結合圖片一看:

觀察一下,進程佔用的虛擬內存一般都多大,大的比如裡面的java進程是2.5個G 這樣,小的就幾十M,比如nginx的進程。而看實際物理內存,最大的就java的266M。這裡綜合一下4G的模型,所以在這裡理解都是4G是針對程序而言的,涉及到多級頁表的知識。

然後是下一個理解:

malloc 是 glibc 中內存分配函數,也是最常用的動態內存分配函數,其內存必須通過 free 進行釋放,否則導致內存泄露。

關於 malloc 獲得虛存空間的實現,與 glibc 的版本有關,但大體邏輯是:

  1. 若分配內存小於 128k ,調用 sbrk() ,將堆頂指針向高地址移動,獲得新的虛存空間。
  2. 若分配內存大於 128k ,調用 mmap() ,在文件映射區域中分配匿名虛存空間。
  3. 這裡討論的是簡單情況,如果涉及並發可能會複雜一些,不過先不討論。

其中 sbrk 就是修改棧頂指針位置,而 mmap 可用於生成文件的映射以及匿名頁面的內存,這裡指的是匿名頁面。

而這個 128k ,是 glibc 的默認配置

從這裡我們可以得到的信息是:內存分配和置換不只是指令代碼,還包括對象,數據之類的。只要是暫時沒用到的都可以把該page置換出去。

下一個理解:

  1. VSZ 並不是每次 malloc 後都增長,是與上一節說的堆頂沒發生變化有關,因為可重用堆頂內剩餘的空間,這樣的 malloc 是很輕量快速的。
  2. 但如果 VSZ 發生變化,基本與分配內存量相當,因為 VSZ 是計算虛擬地址空間總大小。
  3. RSS 的增量很少,是因為 malloc 分配的內存並不就馬上分配實際存儲空間,只有第一次使用,如第一次 memset 後才會分配。(也就是使用才分配的原則,符合虛擬內存的原理)
  4. 由於每個物理內存頁面大小是 4k ,不管 memset 其中的 1k 還是 5k 、 7k ,實際佔用物理內存總是 4k 的倍數。所以 RSS 的增量總是 4k 的倍數
  5. 因此,不是 malloc 後就馬上佔用實際內存,而是第一次使用時發現虛存對應的物理頁面未分配,產生缺頁中斷,才真正分配物理頁面,同時更新進程頁面的映射關係(也就是頁表)。這也是 Linux 虛擬內存管理的核心概念之一。(結合前面的知識,也就是基本上都是malloc後就放在虛擬內存當中,然後用上了再載入到物理內存)

疑問:究竟linux的虛擬內存的頁面置換演算法是怎麼實現的呢?似乎需要看源碼啊

嗯,差不多就到這裡吧。感覺還是得慢慢研究《深入理解計算機系統》這本書啊。這本書纔是權威,看網上的很多都不太靠譜。但是這本書有點難讀,後面慢慢來吧。。。

歡迎交流討論。


推薦閱讀:
相關文章