1. 程式人生 > >【原創】(三)Linux paging_init解析

【原創】(三)Linux paging_init解析

背景

  • Read the fucking source code! --By 魯迅
  • A picture is worth a thousand words. --By 高爾基

說明:

  1. Kernel版本:4.14
  2. ARM64處理器,Contex-A53,雙核
  3. 使用工具:Source Insight 3.5, Visio

1. 介紹

(二)Linux實體記憶體初始化中,可知在paging_init呼叫之前,存放Kernel ImageDTB的兩段實體記憶體區域可以訪問了(相應的頁表已經建立好)。儘管實體記憶體已經通過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_kernelmap_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,