【原創】(三)Linux paging_init解析
背景
Read the fucking source code!
--By 魯迅A picture is worth a thousand words.
--By 高爾基
說明:
- Kernel版本:4.14
- ARM64處理器,Contex-A53,雙核
- 使用工具:Source Insight 3.5, Visio
1. 介紹
從(二)Linux實體記憶體初始化
中,可知在paging_init
呼叫之前,存放Kernel Image
和DTB
的兩段實體記憶體區域可以訪問了(相應的頁表已經建立好)。儘管實體記憶體已經通過memblock_add
新增進系統,但是這部分的實體記憶體到虛擬記憶體的對映還沒有建立,可以通過memblock_alloc
paging_init
的執行。最終頁表建立好後,可以通過虛擬地址去訪問最終的實體地址了。
按照慣例,先上圖,來一張ARM64核心的記憶體佈局圖片吧,最終的佈局如下所示:
開啟探索之旅吧!
2. paging_init
paging_init
原始碼短小精悍,直接貼上來,分模組來介紹吧。
/* * paging_init() sets up the page tables, initialises the zone memory * maps and sets up the zero page. */ void __init paging_init(void) { phys_addr_t pgd_phys = early_pgtable_alloc(); /********(mark 1)*******/ pgd_t *pgd = pgd_set_fixmap(pgd_phys); map_kernel(pgd); /********(mark 2)*******/ map_mem(pgd); /********(mark 3)*******/ /* * We want to reuse the original swapper_pg_dir so we don't have to * communicate the new address to non-coherent secondaries in * secondary_entry, and so cpu_switch_mm can generate the address with * adrp+add rather than a load from some global variable. * * To do this we need to go via a temporary pgd. */ cpu_replace_ttbr1(__va(pgd_phys)); /********(mark 4)*******/ memcpy(swapper_pg_dir, pgd, PGD_SIZE); cpu_replace_ttbr1(lm_alias(swapper_pg_dir)); pgd_clear_fixmap(); memblock_free(pgd_phys, PAGE_SIZE); /* * We only reuse the PGD from the swapper_pg_dir, not the pud + pmd * allocated with it. */ memblock_free(__pa_symbol(swapper_pg_dir) + PAGE_SIZE, SWAPPER_DIR_SIZE - PAGE_SIZE); }
mark 1
:分配一頁大小的實體記憶體存放pgd
;mark 2
:將核心的各個段進行對映;mark 3
:將memblock子系統新增的實體記憶體進行對映;mark 4
:切換頁表,並將新建立的頁表內容替換swappper_pg_dir
頁表內容;
程式碼看起來費勁?圖來了:
下邊將對各個子模組進一步的分析。
3. early_pgtable_alloc
這個模組與FIX MAP
對映區域相關,建議先閱讀前文(二)Linux實體記憶體初始化
先上圖:
FIX MAP
的區域劃分從圖中可以看出來
本函式會先分配實體記憶體,然後借用之前的全域性頁表bm_pte
,建立實體地址到虛擬地址的對映,這次對映的作用是為了去訪問實體記憶體,把記憶體清零,所以它只是一個臨時操作,操作完畢後,會呼叫pte_clear_fixmap()
early_pgtable_alloc
之後,我們看到paging_init
呼叫了pgd_set_fixmap
函式,這個函式呼叫完後,通過memblock_alloc
分配的實體記憶體,最終就會用來存放pgd table
了,這片區域的內容最後也會拷貝到swapper_pg_dir
中去。
4. map_kernel
map_kernel
的主要工作是完成核心中各個段的對映,此外還包括了FIXADDR_START
虛擬地址的對映,如下圖:
對映完成之後,可以看一下具體各個段的區域,以我自己使用的平臺為例:
這些地址資訊也能從System.map
檔案中找到。
aarch64-linux-gnu-objdump -x vmlinux
能檢視更詳細的地址資訊。
5. map_mem
從函式名字中可以看出,map_mem
主要完成的是實體記憶體的對映,這部分的實體記憶體是通過memblock_add
新增到系統中的,當對應的memblock設定了MEMBLOCK_NOMAP
的標誌時,則不對其進行地址對映。
map_mem
函式中,會遍歷memblock中的各個塊,然後呼叫__map_memblock
來完成實際的對映操作。先來一張效果圖:
map_mem
都是將實體地址對映到線性區域中,我們也發現了Kernel Image
中的text, rodata
段映射了兩次,原因是其他的子系統,比如hibernate
,會對映到線性區域中,可能需要線性區域的地址來引用核心的text, rodata
,對映的時候也會限制成了只讀/不可執行
,防止意外修改或執行。
map_kernel
和map_mem
函式中的頁表對映,最終都是呼叫__create_pgd_mapping
函式實現的:
總體來說,就是逐級頁表建立對映關係,同時中間會進行許可權的控制等。
細節不再贅述,程式碼結合圖片閱讀,效果會更佳噢。
6. 頁表替換及記憶體釋放
這部分程式碼不多,不上圖了,看程式碼吧:
/*
* We want to reuse the original swapper_pg_dir so we don't have to
* communicate the new address to non-coherent secondaries in
* secondary_entry, and so cpu_switch_mm can generate the address with
* adrp+add rather than a load from some global variable.
*
* To do this we need to go via a temporary pgd.
*/
cpu_replace_ttbr1(__va(pgd_phys));
memcpy(swapper_pg_dir, pgd, PGD_SIZE);
cpu_replace_ttbr1(lm_alias(swapper_pg_dir));
pgd_clear_fixmap();
memblock_free(pgd_phys, PAGE_SIZE);
/*
* We only reuse the PGD from the swapper_pg_dir, not the pud + pmd
* allocated with it.
*/
memblock_free(__pa_symbol(swapper_pg_dir) + PAGE_SIZE,
SWAPPER_DIR_SIZE - PAGE_SIZE);
簡單來說,將新建立好的pgd頁表內容,拷貝到swapper_pg_dir
中,也就是覆蓋掉之前的臨時頁表了。當拷貝完成後,顯而易見的是,我們可以把paging_init
一開始分配的實體記憶體給釋放掉。
此外,在之前的文章也分析過swapper_pg_dir
頁表存放的時候,是連續存放的pgd, pud, pmd
等,現在只需要複用swapper_pg_dir
,其餘的當然也是可以釋放的了。
好了,點到為止,前路漫漫,離Buddy System,Slab,Malloc以及各種記憶體的騷操作好像還有很遠的樣子,待續吧。
相關推薦
【原創】(三)Linux paging_init解析
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(三)Linux程序排程器-程序切換
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(四)Linux記憶體模型之Sparse Memory Model
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(六)Linux記憶體管理 - zoned page frame allocator - 1
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(七)Linux記憶體管理 - zoned page frame allocator - 2
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(八)Linux記憶體管理 - zoned page frame allocator - 3
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(九)Linux記憶體管理 - zoned page frame allocator - 4
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(十三)Linux記憶體管理之vma/malloc/mmap
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(一)Linux程序排程器-基礎
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(二)Linux程序排程器-CPU負載
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(四)Linux程序排程-組排程及頻寬控制
# 背景 - `Read the fucking source code!` --By 魯迅 - `A picture is worth a thousand words.` --By 高爾基 說明: 1. Kernel版本:4.14 2. ARM64處理器,Contex-A53,雙核 3. 使用工具:S
【原創】(五)Linux程序排程-CFS排程器
# 背景 - `Read the fucking source code!` --By 魯迅 - `A picture is worth a thousand words.` --By 高爾基 說明: 1. Kernel版本:4.14 2. ARM64處理器,Contex-A53,雙核 3. 使用工具:S
【原創】(六)Linux程序排程-實時排程器
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【Mybatis】(三)執行CRUD操作——基於XML實現
在【Mybatis】(一)第一個mybatis例項中,已經初步搭建了 MyBatis 框架,實現了查詢所有記錄的功能,並用 JUnit 進行了單元測試 接下來我們將在此基礎上使用基於XML的方式對錶進行CRUD操作 1、定義EmployeeMapper.xm
IDEA從0搭建SSM專案【圖文】(三)——部署到遠端伺服器執行
系列(一)和(二)實現了本地localhost執行,但是實際生產往往需要我們把專案放到遠端伺服器上執行,本文演示如何具體實現~ 一. 需要具備的知識 1.maven命令 2.linux常用命令 3.linux安裝jdk,tomcat,mysql 二.伺服器 1.
【Java】(三)運算子小結(比較、邏輯、三元運算子)
前面介紹的兩種運算子都比較簡單,下面我們來看一下比較複雜一點的三種運算子:比較運算子、邏輯運算子、三元運算子。 一、比較運算子 又叫關係運算符,用於判斷兩個被運算元的
【Docker】基於例項專案的叢集部署(三)Linux基礎命令
Linux系統作為優秀的企業級伺服器系統,有多處優點: 可靠的安全性 良好的穩定性 完善的網路功能 多使用者任務 豐富的軟體支援 跨平臺的硬體支援 目錄結構 我們可以通過以下結構瞭解Linux的目錄作用: 命令操作
【原創】(十一)Linux記憶體管理slub分配器
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(十二)Linux記憶體管理之vmap與vmalloc
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(十四)Linux記憶體管理之page fault處理
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,