ARM64 Linux 內核頁表的塊映射

2021-01-10 CSDN

作者 | 宋寶華 責編 | 張文

頭圖 | CSDN 下載自視覺中國

內核文檔 Documentation/arm64/memory.rst 描述了 ARM64 Linux 內核空間的內存映射情況,應該是此方面最權威文檔。

以典型的 4K 頁和 48 位虛擬地址為例,整個內核空間的虛擬地址分布如下:

從 ffff000000000000 到 ffff7fffffffffff 是一段針對物理地址的線性映射區,最大支持 128TB 的物理地址空間,這一段地址非常類似 ARM32 的 low memory 映射區。

我們看看這種情況下的頁表,我們既可以用最終的【20:12】對應的 PTE 映射項,以 4K 為單位,進行虛擬地址到物理地址的映射;又可以以【29:21】對應的 PMD 映射項,以 2M 為單位,進行虛擬地址到物理地址的映射。

對於用戶空間的虛擬地址而言,當我們進行的是 PMD 映射的時候,我們得到的是 Huge Page,ARM64 的 2MB 的 huge page,在虛擬和物理上都連續,它在實踐工程中的好處是,可以減小 TLB miss。因為如果進行了 2MB 的映射,整個 2MB 不再需要 PTE,映射關係大為減小。

對於內核空間而言,從 ffff000000000000 到 ffff7fffffffffff 的這段虛擬地址,如果與物理地址進行的是一種 PMD 映射的話,顯然也可以達到同樣的效果。

但是,這不意味著它們就是 Huge Page。

眾所周知,內核開機把物理地址往虛擬地址進行線性映射,並不意味著這片內存被內核拿走了,它只是進行了一種映射,以便日後調用 kmalloc(),get_free_pages()等 API 申請的內存是直接已經有虛實映射的。所以,即便內核進行的就是 PMD 映射,在內存的分割上,還是可以以 4K 為單位的:

所以,即便我們在內核空間進行 PMD 映射,裡面的每個藍色圓圈(一個 4K頁),還是可以被單獨分配的,這種分配可以是 kmalloc、vmalloc,用戶態的 malloc 等。

內核態進行的 PMD 映射,不意味著相關的 2MB 成為了 huge page。

它純粹只是為了服務於當內核以線性映射的虛擬地址訪問該物理地址的時候(我們認為內核大多數時候是用這個線性映射的虛擬地址的),減小 TLB miss。

當然,更牛的情況下,內核應該也可以直接用【38:30】位的 PUD 來進行映射,這樣映射關係是 1GB 的,則整個 1GB 後面佔 TLB 的時候,只需要佔一個入口。

當然,如果用戶態的虛實映射是這樣的,用戶實際得到了一個 1GB 的巨頁。

但是對於內核的線性映射區域而言,即便我們進行了 1GB 的 PUD 映射,這 1G 內部就可以進一步切割為 4KB 頁或者 2MB 的巨頁。

記住:內核態的線性映射區的映射只是個映射關係,不是個分配關係。比如下面的 1GB 的內核線性映射的 1GB 區域,仍然可以被 4K 分配走,或者被用戶以 huge page 以 2MB 為單位分配走:

我們需要一個真實的調試手段來驗證我們的想法,這個調試手段就是 PTDUMP(Page Table Dump),相關的代碼在 ARM64 內核的:

arch/arm64/mm/ptdump.c和ptdump_debugfs.c

我們把它們全部選中,這樣我們可以得到一個 debugfs 接口:

/sys/kernel/debug/kernel_page_tables

來獲知內核態頁表的情況。

我用 qemu 啟動了一個 4GB 內存的 ARM64 虛擬機,可以看到前 1GB 的虛擬地址空間大多數是 PMD 和 PTE 映射,後面的 3GB,全是 PUD 映射:

我的內核啟動參數加了 rodata=0:

$ cat /proc/cmdline root=/dev/vda2 rw console=ttyAMA0 ip=dhcp rodata=0

原因是內核在幾種情況下,是不會做這種 PMD 和 PUD 映射的,相關代碼見於:

rodata_full 在默認情況下總是成立的,它對應著內核的一個 Config 選項 CONFIG_RODATA_FULL_DEFAULT_ENABLED, "Apply r/o permissions of VM areas also to their linear aliases",這個選項提高了內核的安全性,但是減小了內核的性能。

我在內核啟動參數加的 rodata=0 實際上是讓 rodata_full 為 false。

如果我把這個 kernel 啟動選項去掉,我得到的內核頁表是完全不一樣,線性映射區也全部是 PTE 映射:

最後,值得一提的是,不僅線性映射區可以使用 PMD 映射,vmemmap 映射區也是在 4K 頁面情況下,默認用 PMD 映射的:

字節跳動的宋牧春童鞋發了一個 patchset,企圖在用戶分得巨頁的情況下,刪除巨頁內部的 4KB 的小 page 佔用的 page struct 的內存消耗,這個 patchset 在聖誕節前目前發到了 V11:

https://lore.kernel.org/linux-mm/20201222142440.28930-1-songmuchun@bytedance.com/

在這個 patchset 中,它就需要拆分 vmemmap 的 PMD 映射為 PTE 映射:

這個 patchset 的原理建立在,當內核以 4KB 分頁的時候,每個 page 需要 64 字節的 page struct。

但是,當用戶把它分配為巨頁的時候,我們不再需要一個個 4KB 單獨用 page struct 描述,對於這種 compound page 的情況,我們應該可以把後面的 page struct 的內存直接釋放掉,因為情況完全是雷同的,這樣可以省下不少內存。

看到牧春童鞋從一個青蔥少年成長為這方面的大牛,我真的替他高興和驕傲。這同時也激勵我必須保持奮鬥姿態,2021 年要不停歇地學習進步,爭取趕上牧春童鞋。

程式設計師如何避免陷入「內卷」、選擇什麼技術最有前景,中國開發者現狀與技術趨勢究竟是什麼樣?快來參與「2020中國開發者大調查」,更有豐富獎品送不停!

相關焦點

  • ARM Linux根文件系統Root Filesystem的製作
    說Busybox和arm-linux-gcc有兼容性問題,不過我覺得那是比較低版本的時代問題了,我用Busybox 1.8.2和arm-linux-gcc 3.4.1/3.3.2都可以。解壓縮以後找到Makefile裡面的ARCH和CROSS_COMPILE,改成:ARCH ?= armCROSS_COMPILE ?
  • ARM開發板上uClinux內核移植
    通過這段程序,我們可以初始化硬體設備、建立內存空間的映射圖,從而將系統的軟硬體環境帶到一個合適的狀態,以便為最終調用作業系統內核准備好正確的環境。因此,正確建立uClinux的移植的前提條件是具備一個與uClinux配套、易於使用的 Bootloader。
  • Linux2.6內核驅動移植參考
    作者:晏渭川 隨著Linux2.6的發布,由於2.6內核做了教的改動,各個設備的驅動程序在不同程度上要 進行改寫。為了方便各位Linux愛好者我把自己整理的這分文檔share出來。該文當列舉 了2.6內核同以前版本的絕大多數變化,可惜的是由於時間和精力有限沒有詳細列出各個 函數的用法。
  • 從RTOS到Linux的應用移植
    而外設I/O資源是不在Linux內核虛擬地址空間中的(如SRAM或硬體接口寄存器等),若需要訪問某外設I/O資源,必須先將其物理地址映射到內核虛擬地址空間中,然後才能在內核空間中訪問它。  Linux內核訪問外設I/O資源的方式有兩種:靜態映射(map_desc)和動態映射(ioremap)。
  • 嵌入式Linux開發環境的搭建之:嵌入式開發環境的搭建
    其中,binutils主要用於生成一些輔助工具,如objdump、as、ld等;gcc是用來生成交叉編譯器的,主要生成arm-linux-gcc交叉編譯工具(應該說,生成此工具後已經搭建起了交叉編譯環境,可以編譯Linux內核了,但由於沒有提供標準用戶函數庫,用戶程序還無法編譯);glibc主要是提供用戶程序所使用的一些基本的函數庫。這樣,交叉編譯環境就完全搭建起來了。
  • ARM 浮點運算詳解
    編譯:arm-hisiv200-linux-gcc -c -Wall fcpu.c -o fcpu.oarm-hisiv200-linux-gcc fcpu.o -o FCPU -L./運行,則得到32位浮點數加1024次所需要時間。
  • 從串口驅動到Linux驅動模型,想轉Linux的必會!
    /linux/driver/tty/serial/samsung.c中。好了。我們從這個目錄結構開始。說明大概的tty子系統驅動模型。首先。最前面的linux是內核代碼的根目錄。如圖所示。至此。我們面臨一個問題。linux內核是什麼。Linux內核是什麼?Linux是一種開源電腦作業系統內核。
  • arm開發板與樹莓派有什麼區別
    更重要的是,因為用戶體驗差和工作的原因,我的那塊開發板在最初折騰幾天後我就很少再用了,並且在兩年前賣掉了。因此我對 arm 開發板的印象並不好,所以在之後的時間,我也沒有關注樹莓派的更新換代。   它使用ARM處理器晶片,和linux作業系統或windows作業系統,是一個很小體積的桌面電腦。   樹莓派由註冊於英國的慈善組織「Raspberry Pi 基金會」開發,Eben·Upton/埃·厄普頓為項目帶頭人。
  • arm 微學術 架構_arm彙編 - CSDN
    @ goto 當前位置執行,構成執行死循環.end @ 代表整個彙編文件的結束$: arm-cortex_a9-linux-gnueabi-as test.s -o test.o // 編譯彙編代碼$: arm-cortex_a9-linux-gnueabi-objdump -S test.o > 1.
  • ARM指令學習筆記
    ARM946E-S是一種有5級流水線,集成有Thumb擴展功能、調試功能和哈佛總線的內核。在同樣工藝下,它是ARM7TDMI性能的兩倍以上。有了個簡單了解之後,開始投入到了arm指令的學習。arm7TDMI(-S)指令系統有兩套指令集,分別是32位的Arm指令集和16位的thumb指令集。簡單點說:arm支持arm內核的所有特點,具有高效、快速的特點;而thumb指令集靈活、小巧。
  • 技術文檔丨如何為Apollo安裝低時延/實時內核
    下面是安裝Ubuntu低時延內核的步驟:1. 安裝最新的低時延內核及其頭文件。1sudo apt-get update2sudo apt-getinstall linux-image-$(uname -r)-lowlatency linux-headers-$(uname -r)-lowlatency<左右滑動以查看完整代碼>注意:如果在執行了sudo apt-get update後通過apt list --
  • 一張圖看懂Linux內核中Percpu變量的實現
    但你知道嗎,不僅是在程式語言中,在linux內核中,也有一個類似的機制,用來實現類似的目的,它叫做percpu變量。percpu變量,顧名思義,就是對於同一個變量,每個cpu都有自己的一份,它可以被用來存放一些cpu獨有的數據,比如cpu的id,cpu上正在運行的線程等等,因該機制可以非常方便的解決一些特定問題,所以在內核編程中被廣泛使用。
  • 升級Ubuntu Linux 內核的幾種不同方法
    這個指南裡介紹了 7 種為 Ubuntu 升級 Linux 內核的不同方法。這 7 種方法裡,有 5 種需要重啟系統來使新內核生效,其他兩種則不用。-- Sk這個指南裡介紹了 7 種為 Ubuntu 升級 Linux 內核的不同方法。這 7 種方法裡,有 5 種需要重啟系統來使新內核生效,其他兩種則不用。
  • 嵌入式Linux的GDB調試環境建立
    嵌入式Linux的GDB調試環境由Host和Target兩部分組成,Host端使用arm-linux-gdb,Target Board端使用gdbserver。
  • net-snmp移植到arm
    關於net-snmp的移植,是基於靜態編譯的,動態的沒有做成功,在arm上的移植過程大致如下:如果沒有涉及到擴展agent,則藍色字體不需要理會。tar -xzvf net-snmp-5.4.1.tar.gz,若想擴展agent,則可以將此模塊的.c和.h文件先放到net-snmp-5.4.4/agent/mibgroup下一起配置編譯;例如我這裡想用代理讀寫encoder模塊,那麼先將寫好的encoder源文件複製到net-snmp-5.4.4/agent/mibgroup路徑下;2.CC=arm-linux-gcc
  • Linux系統的Linux應該怎麼讀?正確讀法在這裡,很多人都讀錯了!
    3、有人綜合網上和linux自己的讀音,概括出幾個自認為最合適也最通用的讀法:/li'n^ks/(「裡那克斯」)或/'li:nэks/(「裡訥克斯」)或/li'nju:ks/(「裡紐克斯」)。4、這幾個應該是誰都聽得懂的。至於哪個比較正宗,當然是linux的原因。但事實上使用linux哪種讀法的人似乎都不在少數。
  • ARM學習筆記--GPIO接口
    -linux-gcc -g -c -o led_on.o led_on.Sarm-linux-ld -Ttext 0x0000000 -g led_on.o -o led_on_elfarm-linux-objcopy -O binary -S led_on_elf led_on.binclean:rm -f led_on.bin
  • Linux基礎命令之:實驗內容及小結
    命令為:cd/mnt/win;ls(8)在/usr/local下建一個名為arm的目錄。命令為:mkdir/usr/local/arm(9)將cross-3.3.2.tar.bz2複製到剛剛創建的目錄中。
  • 嵌入式linux新手入門手記-準備qt5.4.1開發平臺
    /qtbase/mkspecs/linux-arm-gnueabi-g++/qmake.conf。mkspecs目錄下有很多的保存.conf文件的目錄,分別對應不同的作業系統和交叉編譯平臺,我這裡使用linux-arm-gnueabi-g++平臺的配置文件進行交叉編譯。