<code>Read the fucking source code!</code> --By 魯迅
<code>A picture is worth a thousand words.</code> --By 高爾基
說明:
Kernel版本:4.14
ARM64處理器,Contex-A53,雙核
使用工具:Source Insight 3.5, Visio
從<code>(二)Linux實體記憶體初始化</code>中,可知在<code>paging_init</code>調用之前,存放<code>Kernel Image</code>和<code>DTB</code>的兩段實體記憶體區域可以通路了(相應的頁表已經建立好)。盡管實體記憶體已經通過<code>memblock_add</code>添加進系統,但是這部分的實體記憶體到虛拟記憶體的映射還沒有建立,可以通過<code>memblock_alloc</code>配置設定一段實體記憶體,但是還不能通路,一切還需要等待<code>paging_init</code>的執行。最終頁表建立好後,可以通過虛拟位址去通路最終的實體位址了。
按照慣例,先上圖,來一張ARM64核心的記憶體布局圖檔吧,最終的布局如下所示:

開啟探索之旅吧!
<code>paging_init</code>源代碼短小精悍,直接貼上來,分子產品來介紹吧。
<code>mark 1</code>:配置設定一頁大小的實體記憶體存放<code>pgd</code>;
<code>mark 2</code>:将核心的各個段進行映射;
<code>mark 3</code>:将memblock子系統添加的實體記憶體進行映射;
<code>mark 4</code>:切換頁表,并将建立立的頁表内容替換<code>swappper_pg_dir</code>頁表内容;
代碼看起來費勁?圖來了:
下邊将對各個子子產品進一步的分析。
這個子產品與<code>FIX MAP</code>映射區域相關,建議先閱讀前文<code>(二)Linux實體記憶體初始化</code>
先上圖:
<code>FIX MAP</code>的區域劃分從圖中可以看出來
本函數會先配置設定實體記憶體,然後借用之前的全局頁表<code>bm_pte</code>,建立實體位址到虛拟位址的映射,這次映射的作用是為了去通路實體記憶體,把記憶體清零,是以它隻是一個臨時操作,操作完畢後,會調用<code>pte_clear_fixmap()</code>來清除映射。
<code>early_pgtable_alloc</code>之後,我們看到<code>paging_init</code>調用了<code>pgd_set_fixmap</code>函數,這個函數調用完後,通過<code>memblock_alloc</code>配置設定的實體記憶體,最終就會用來存放<code>pgd table</code>了,這片區域的内容最後也會拷貝到<code>swapper_pg_dir</code>中去。
<code>map_kernel</code>的主要工作是完成核心中各個段的映射,此外還包括了<code>FIXADDR_START</code>虛拟位址的映射,如下圖:
映射完成之後,可以看一下具體各個段的區域,以我自己使用的平台為例:
這些位址資訊也能從<code>System.map</code>檔案中找到。
<code>aarch64-linux-gnu-objdump -x vmlinux</code>能檢視更詳細的位址資訊。
從函數名字中可以看出,<code>map_mem</code>主要完成的是實體記憶體的映射,這部分的實體記憶體是通過<code>memblock_add</code>添加到系統中的,當對應的memblock設定了<code>MEMBLOCK_NOMAP</code>的标志時,則不對其進行位址映射。
<code>map_mem</code>函數中,會周遊memblock中的各個塊,然後調用<code>__map_memblock</code>來完成實際的映射操作。先來一張效果圖:
<code>map_mem</code>都是将實體位址映射到線性區域中,我們也發現了<code>Kernel Image</code>中的<code>text, rodata</code>段映射了兩次,原因是其他的子系統,比如<code>hibernate</code>,會映射到線性區域中,可能需要線性區域的位址來引用核心的<code>text, rodata</code>,映射的時候也會限制成了<code>隻讀/不可執行</code>,防止意外修改或執行。
<code>map_kernel</code>和<code>map_mem</code>函數中的頁表映射,最終都是調用<code>__create_pgd_mapping</code>函數實作的:
總體來說,就是逐級頁表建立映射關系,同時中間會進行權限的控制等。
細節不再贅述,代碼結合圖檔閱讀,效果會更佳噢。
這部分代碼不多,不上圖了,看代碼吧:
簡單來說,将建立立好的pgd頁表内容,拷貝到<code>swapper_pg_dir</code>中,也就是覆寫掉之前的臨時頁表了。當拷貝完成後,顯而易見的是,我們可以把<code>paging_init</code>一開始配置設定的實體記憶體給釋放掉。
此外,在之前的文章也分析過<code>swapper_pg_dir</code>頁表存放的時候,是連續存放的<code>pgd, pud, pmd</code>等,現在隻需要複用<code>swapper_pg_dir</code>,其餘的當然也是可以釋放的了。
好了,點到為止,前路漫漫,離Buddy System,Slab,Malloc以及各種記憶體的騷操作好像還有很遠的樣子,待續吧。
作者:LoyenWang
出處:https://www.cnblogs.com/LoyenWang/
公衆号:<b>LoyenWang</b>
版權:本文版權歸作者和部落格園共有
轉載:歡迎轉載,但未經作者同意,必須保留此段聲明;必須在文章中給出原文連接配接;否則必究法律責任