Linux分頁機制之概述--Linux記憶體管理(六)

来源:https://www.cnblogs.com/linhaostudy/archive/2018/11/25/10015197.html
-Advertisement-
Play Games

1 分頁機制 在虛擬記憶體中,頁表是個映射表的概念, 即從進程能理解的線性地址(linear address)映射到存儲器上的物理地址(phisical address). 很顯然,這個頁表是需要常駐記憶體的東西, 以應對頻繁的查詢映射需要(實際上,現代支持VM的處理器都有一個叫TLB的硬體級頁表緩存部 ...


1 分頁機制

在虛擬記憶體中,頁表是個映射表的概念, 即從進程能理解的線性地址(linear address)映射到存儲器上的物理地址(phisical address).

很顯然,這個頁表是需要常駐記憶體的東西, 以應對頻繁的查詢映射需要(實際上,現代支持VM的處理器都有一個叫TLB的硬體級頁表緩存部件,本文不討論)。

1.1 為什麼使用多級頁表來完成映射

但是為什麼要使用多級頁表來完成映射呢?

用來將虛擬地址映射到物理地址的數據結構稱為頁表, 實現兩個地址空間的關聯最容易的方式是使用數組, 對虛擬地址空間中的每一頁, 都分配一個數組項. 該數組指向與之關聯的頁幀,但這會引發一個問題, 例如, IA-32體繫結構使用4KB大小的頁, 在虛擬地址空間為4GB的前提下, 則需要包含100萬項的頁表. 這個問題在64位體繫結構下, 情況會更加糟糕. 而每個進程都需要自身的頁表, 這回導致系統中大量的所有記憶體都用來保存頁表.

設想一個典型的32位的X86系統,它的虛擬記憶體用戶空間(user space)大小為3G,並且典型的一個頁表項(page table entry, pte)大小為4 bytes,每一個頁(page)大小為4k bytes。那麼這3G空間一共有(3G/4k=)786432個頁面,每個頁面需要一個pte來保存映射信息,這樣一共需要786432個pte!

如何存儲這些信息呢?一個直觀的做法是用數組來存儲,這樣每個頁能存儲(4k/4=)1K個,這樣一共需要(786432/1k=)768個連續的物理頁面(phsical page)。而且,這隻是一個進程,如果要存放所有N個進程,這個數目還要乘上N! 這是個巨大的數目,哪怕記憶體能提供這樣數量的空間,要找到連續768個連續的物理頁面在系統運行一段時間後碎片化的情況下,也是不現實的。

為減少頁表的大小並容許忽略不需要的區域, 電腦體繫結構的涉及會將虛擬地址分成多個部分. 同時虛擬地址空間的大部分們區域都沒有使用, 因而頁沒有關聯到頁幀, 那麼就可以使用功能相同但記憶體用量少的多的模型: 多級頁表

但是新的問題來了, 到底採用幾級頁表合適呢?

1.2 32位系統中2級頁表

從80386開始, intel處理器的分頁單元是4KB的頁, 32位的地址空間被分為3部分

單元 描述
頁目錄表Directory 最高10位
頁中間表Table 中間10位
頁內偏移 最低12位

即頁表被劃分為頁目錄表Directory和頁中間表Tabl兩個部分

此種情況下, 線性地址的轉換分為兩步完成.

  • 第一步, 基於兩級轉換表(頁目錄表和頁中間表), 最終查找到地址所在的頁幀

  • 第二步, 基於偏移, 在所在的頁幀中查找到對應偏移的物理地址

使用這種二級頁表可以有效的減少每個進程頁表所需的RAM的數量. 如果使用簡單的一級頁表, 那將需要高達2^20個頁表, 假設每項4B, 則共需要占用2^20 * 4B = 4MB的RAM來表示每個進程的頁表. 當然我們並不需要映射所有的線性地址空間(32位機器上線性地址空間為4GB), 內核通常只為進程實際使用的那些虛擬記憶體區請求頁表來減少記憶體使用量.

1.3 64位系統中的分頁

正常來說, 對於32位的系統兩級頁表已經足夠了, 但是對於64位系統的電腦, 這遠遠不夠.

首先假設一個大小為4KB的標準頁. 因為1KB覆蓋2^10個地址的範圍, 4KB覆蓋2^12個地址, 所以offset欄位需要12位.

這樣線性地址空間就剩下64-12=52位分配給頁中間表Table和頁目錄表Directory. 如果我們現在決定僅僅使用64位中的48位來定址(這個限制其實已經足夠了, 2^48=256TB, 即可達到256TB的定址空間). 剩下的48-12=36位被分配給Table和Directory欄位. 即使我們現在決定位兩個欄位各預留18位, 那麼每個進程的頁目錄和頁表都包含2^18個項, 即超過256000個項.

基於這個原因, 所有64位處理器的硬體分頁系統都使用了額外的分頁級別. 使用的級別取決於處理器的類型

平臺名稱 頁大小 定址所使用的位數 分頁級別數 線性地址分級
alpha 8KB 43 3 10 + 10 + 10 + 13
ia64 4KB 39 3 9 + 9 + 9 + 12
ppc64 4KB 41 3 10 + 10 + 9 + 12
sh64 4KB 41 3 10 + 10 + 9 + 12
x86_64 4KB 48 4 9 + 9 + 9 + 9 + 12

1.4 Linux中的分頁

層次話的頁表用於支持對大地址空間快速, 高效的管理. 因此linux內核堆頁表進行了分級.

前面我們提到過, 對於32位系統中, 兩級頁表已經足夠了. 但是64位需要更多數量的分頁級別.

為了同時支持適用於32位和64位的系統, Linux採用了通用的分頁模型. 在Linux-2.6.10版本中, Linux採用了三級分頁模型. 而從2.6.11開始普遍採用了四級分頁模型.

目前的內核的記憶體管理總是假定使用四級頁表, 而不管底層處理器是否如此.

單元 描述
頁全局目錄 Page GlobalDirectory
頁上級目錄 Page Upper Directory
頁中間目錄 Page Middle Directory
頁表 Page Table
頁內偏移 Page Offset

Linux不同於其他的操作系統, 它把電腦分成獨立層(體繫結構無關)/依賴層(體繫結構相關)兩個層次. 對於頁面的映射和管理也是如此. 頁表管理分為兩個部分, 第一個部分依賴於體繫結構, 第二個部分是體繫結構無關的. 所有數據結構幾乎都定義在特定體繫結構的文件中. 這些數據結構的定義可以在頭文件arch/對應體系/include/asm/page.harch/對應體系/include/asm/pgtable.h中找到. 但是對於AMD64和IA-32已經統一為一個體繫結構. 但是在處理頁表方面仍然有很多的區別, 因為相關的定義分為兩個不同的文件arch/x86/include/asm/page_32.harch/x86/include/asm/page_64.h, 類似的也有pgtable_xx.h .

2 頁表

Linux內核通過四級頁表將虛擬記憶體空間分為5個部分(4個頁表項用於選擇頁, 1個索引用來表示頁內的偏移). 各個體繫結構不僅地址長度不同, 而且地址字拆分的方式也不一定相同. 因此內核使用了巨集用於將地址分解為各個分量.

其他內容請參照博主的另外兩篇博客, 我就不羅嗦了

深入理解電腦系統-之-記憶體定址(五)–頁式存儲管理, 詳細講解了傳統的頁式存儲管理機制

深入理解電腦系統-之-記憶體定址(六)–linux中的分頁機制, 詳細的講解了Linux內核分頁機制的實現機制

3 Linux分頁機制的演變

3.1 Linux的頁表實現

由於程式存在局部化特征, 這意味著在特定的時間內只有部分記憶體會被頻繁訪問,具體點,進程空間中的text段(即程式代碼), 堆, 共用庫,棧都是固定在進程空間的某個特定部分,這樣導致進程空間其實是非常稀疏的, 於是,從硬體層面開始,頁表的實現就是採用分級頁表的方式,Linux內核當然也這麼做。所謂分級簡單說就是,把整個進程空間分成區塊,區塊下麵可以再細分,這樣在記憶體中只要常駐某個區塊的頁表即可,這樣可以大量節省記憶體。

3.2 Linux最初的二級頁表

Linux最初是在一臺i386機器上開發的,這種機器是典型的32位X86架構,支持兩級頁表

一個32位虛擬地址如上圖劃分。當在進行地址轉換時,結合在CR3寄存器中存放的頁目錄(page directory, PGD)的這一頁的物理地址,再加上從虛擬地址中抽出高10位叫做頁目錄表項(內核也稱這為pgd)的部分作為偏移, 即定位到可以描述該地址的pgd;

從該pgd中可以獲取可以描述該地址的頁表的物理地址,再加上從虛擬地址中抽取中間10位作為偏移, 即定位到可以描述該地址的pte;

在這個pte中即可獲取該地址對應的頁的物理地址, 加上從虛擬地址中抽取的最後12位,即形成該頁的頁內偏移, 即可最終完成從虛擬地址到物理地址的轉換。

從上述過程中,可以看出,對虛擬地址的分級解析過程,實際上就是不斷深入頁表層次,逐漸定位到最終地址的過程,所以這一過程被叫做page talbe walk。

至於這種做法為什麼能節省記憶體,舉個更簡單的例子更容易明白。比如要記錄16個球場的使用情況,每張紙能記錄4個場地的情況。採用4+4+4+4,共4張紙即可記錄,但問題是球場使用得很少,有時候一整張紙記錄的4個球場都沒人使用。於是,採用4 x 4方案,即把16個球場分為4組,同樣每張紙剛好能記錄4組情況。這樣,使用一張紙A來記錄4個分組球場情況,當某個球場在使用時,只要額外使用多一張紙B來記錄該球場,同時,在A上記錄”某球場由紙B在記錄”即可。這樣在大部分球場使用很少的情況下,只要很少的紙即困記錄,當有球場被使用,有需要再用額外的紙來記錄,當不用就擦除。這裡一個很重要的前提就是:局部性。

3.3 Linux的三級頁表

當X86引入物理地址擴展(Pisycal Addrress Extension, PAE)後,可以支持大於4G的物理記憶體(36位),但虛擬地址依然是32位,原先的頁表項不適用,它實際多4 bytes被擴充到8 bytes,這意味著,每一頁現在能存放的pte數目從1024變成512了(4k/8)。相應地,頁表層級發生了變化,Linus新增加了一個層級,叫做頁中間目錄(page middle directory, PMD), 變成:

欄位 描述
cr3 指向一個PDPT
PGD 指向PDPT中4個項中的一個
PMD 指向頁目錄中512項中的一個
PTE 指向頁表中512項中的一個
page offset 4KB頁中的偏移

實際的page table walk依然類似,只不過多了一級

現在就同時存在2級頁表和3級頁表,在代碼管理上肯定不方便。巧妙的是,Linux採取了一種抽象方法:所有架構全部使用3級頁表: 即PGD -> PMD -> PTE。那隻使用2級頁表(如非PAE的X86)怎麼辦?

辦法是針對使用2級頁表的架構,把PMD抽象掉,即虛設一個PMD表項。這樣在page table walk過程中,PGD本直接指向PTE的,現在不了,指向一個虛擬的PMD,然後再由PMD指向PTE。這種抽象保持了代碼結構的統一。

3.4 Linux的四級頁表

硬體在發展,3級頁表很快又捉襟見肘了,原因是64位CPU出現了, 比如X86_64, 它的硬體是實實在在支持4級頁表的。它支持48位的虛擬地址空間1。如下:

欄位 描述
PML4 指向一個PDPT
PGD 指向PDPT中4個項中的一個
PMD 指向頁目錄中512項中的一個
PTE 指向頁表中512項中的一個
page offset 4KB頁中的偏移

Linux內核針為使用原來的3級列表(PGD->PMD->PTE),做了折衷。即採用一個唯一的,共用的頂級層次,叫PML4[2]。這個PML4沒有編碼在地址中,這樣就能套用原來的3級列表方案了。不過代價就是,由於只有唯一的PML4, 定址空間被局限在(239=)512G, 而本來PML4段有9位, 可以支持512個PML4表項的。現在為了使用3級列表方案,只能限制使用一個, 512G的空間很快就又不夠用了,解決方案呼之欲出。

在2004年10月,當時的X86_64架構代碼的維護者Andi Kleen提交了一個叫做4level page tables for Linux的PATCH系列,為Linux內核帶來了4級頁表的支持。在他的解決方案中,不出意料地,按照X86_64規範,新增了一個PML4的層級, 在這種解決方案中,X86_64擁一個有512條目的PML4, 512條目的PGD, 512條目的PMD, 512條目的PTE。對於仍使用3級目錄的架構來說,它們依然擁有一個虛擬的PML4,相關的代碼會在編譯時被優化掉。 這樣,就把Linux內核的3級列表擴充為4級列表。這系列PATCH工作得不錯,不久被納入Andrew Morton的-mm樹接受測試。

不出意外的話,它將在v2.6.11版本中釋出。但是,另一個知名開發者Nick Piggin提出了一些看法,他認為Andi的Patch很不錯,不過他認為最好還是把PGD作為第一級目錄,把新增加的層次放在中間,並給出了他自己的Patch:alternate 4-level page tables patches。Andi更想保持自己的PATCH, 他認為Nick不過是玩了改名的游戲,而且他的PATCH經過測試很穩定,快被合併到主線了,不宜再折騰。

不過Linus卻表達了對Nick Piggin的支持,理由是Nick的做法conceptually least intrusive。畢竟作為Linux的扛把子,穩定對於Linus來說意義重大。

最終,不意外地,最後Nick Piggin的PATCH在v2.6.11版本中被合併入主線。在這種方案中,4級頁表分別是:PGD -> PUD -> PMD -> PTE。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • parted不同於fdisk(<2T)它比fdiskf更加靈活,fdisk需保持後才能生效,而parted是分區後直接生效! 磁碟分區步驟: 1、parted /dev/sdb #進入磁碟分區 2、help #查看相關的命令 3、mklabel #調整分區格式 4、yes 5、mkpart prim ...
  • 逆向映射(reverse mapping)技術有助於從虛擬記憶體頁跟蹤到對應的物理記憶體頁; 缺頁處理(page fault handling)允許從塊設備按需讀取數據填充虛擬地址空間。 一、簡介 用戶虛擬地址空間的管理比內核地址空間的管理複雜: 每個應用程式都有自身的地址空間,與所有其他應用程式分隔開 ...
  • ^H不是H鍵的意思,是backspace 主要是當你的終端backspace有問題的時候才需要設置 在linux環境下使用sqlplus,在回刪(backspace)時往往會出現 一串的亂碼。出現亂碼是由於oracle的sqlplus不使用gnu的readline庫造成的。 解決方法有2種: 1.  ...
  • 作為一個用.net的渣渣,常年混跡在window平臺下,對Linux啥都不懂。隨著.net core開源、跨平臺後,也開始學習下linux。 在Desktop/Webs下放了一個index.html的靜態頁面,想著用nginx配置下路徑。打開配置一看,這不就是修改下路徑嘛,挺簡單的。 結果修改後重啟 ...
  • 1、檢查是否安裝 rpm -aq sudo rsyslog #檢驗是否安裝此軟體 ***如果沒有需執行(yum install sudo rsyslog -y)安裝*** 2、配置審計 echo "Defaults logfile = /var/log/sudo.log" >> /etc/sudoe ...
  • CentOS 7.3預設使用的是firewall作為防火牆,這裡改為iptables防火牆。 1:關閉firewall: systemctl stop firewalld.service systemctl disable firewalld.service systemctl mask firew ...
  • 目錄參數所在/etc/sudoers 1、Host_Alias定義主機別名 例:Host_Alias FILESERVERS = fs1,fs2 #註意“=”號兩邊要有空格隔開 ***由於現今linux系統都是在windows系統下通過虛擬機來統一管理和操作,所以一般主機別名不用*** 2、User ...
  • 本章節主要講了 Linux 系統下的關於文件I/O操作的幾個函數:open、read、write、lseek、close 的使用和需要註意的一些細節。接著,又介紹了多進程見如何共用文件。下麵開始知識點梳理。 文件描述符 對於內核來說,所有打開的文件,都是通過文件描述符來引用。當打開或創建一個新的文件 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...