共計 2981 個字符,預計需要花費 8 分鐘才能閱讀完成。
本篇內容主要講解“Linux 內核反向映射機制是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓丸趣 TV 小編來帶大家學習“Linux 內核反向映射機制是什么”吧!
1. 反向映射的發展
實際上在早期的 Linux 內核版本中是沒有反向映射的這個概念的,那個時候為了找到一個物理頁面對應的頁表項就需要遍歷系統中所有的 mm 組成的鏈表,然后對于每一個 mm 再遍歷每一個 vma,然后查看這個 vma 是否映射了這頁,這個過程極其漫長而低效,有的時候不得不遍歷完所有的 mm 然后才能找映射到這個頁的所有 pte。
后來人們發現了這個問題,就再描述物理頁面的 page 結構體中增加一個指針的方式來解決,通過這個指針來找到一個描述映射這個頁的所有 pte 的數組結構,這對于反向映射查找所有 pte 易如反掌,但是帶來的是浪費內存的問題。
接著就在 2.6 內核的時候,內核大神們想到了復用 page 結構中的 mapping 字段,然后通過紅黑樹的方式來組織所有映射這個頁的 vma, 形成了匿名頁和文件頁的反向映射機制。
如下為匿名頁反向映射圖解:
如下為文件頁反向映射圖解:
但是后來匿名頁的反向映射遇到了效率和鎖競爭激烈問題,就促使了目前使用的通過 avc 的方式聯系各層級反向映射結構然后將鎖的粒度降低的這種方式??梢钥吹椒聪蛴成涞陌l展是伴隨著 Linux 內核的發展而發展,是一個不斷進行優化演進的過程。
2. 反向映射應用場景
那么為何在 Linux 內核中需要反向映射這種機制呢? 它究竟為了解決什么樣的問題而產生的呢?
試想有如下場景:
(1) 一個物理頁面被多個進程的 vma 所映射,系統過程中發生了內存不足,需要回收一些頁面,正好發現這個頁面是適合我們回收利用的,我們能夠直接把這個頁面還給伙伴系統嗎? 答案肯定是不能。因為這個頁面被很多個進程所共享,我們必須做的事情就是斷開這個頁面的所以映射關系,這就是反向映射所做的事情。
(2) 一些情況我們需要將一個頁面遷移到另一個頁面,但是牽一發而動全身,可能有一些進程已經映射這個即將要遷移的頁面到自己的 vma 中,那么這個時候同樣需要我們知道究竟這個頁面被哪些 vma 所映射呢? 這同樣是反向映射所做的事情。
實際上,反向映射的主要應用場景為內存回收和頁面遷移,當系統發生內存回收和頁面遷移的時候,對于每一個候選頁 Linux 內核都會判斷是否為映射頁,如果是,就會調用 try_to_unmap 來解除頁表映射關系,本文也主要來從 try_to_unmap 函數來解讀反向映射機制。
如果我們在細致到其他的內核子系統會發現,在內存回收,內存碎片整理,CMA, 巨型頁,頁遷移等各個場景中都能發現反向映射所做的關鍵性的工作,所有理解反向映射機制在 Linux 內核中的實現是理解掌握這些子系統的基礎和關鍵性所在,否則你即將不能理解這些技術背后的脊髓所在,所以說理解反向映射這種機制對于理解 Linux 內核內存管理是至關重要的!!!
3. 匿名頁的反向映射
匿名頁的共享主要發生在父進程 fork 子進程的時候,父 fork 子進程時,會復制所有 vma 給子進程,并通過調用 dup_mmap- anon_vma_fork 建立子進程的 rmap 以及和長輩進程 rmap 關系結構:
主要通過 anon_vma 這個數據結構體中的紅黑樹將共享父進程的頁的所有子進程的 vma 聯系起來 (通過 anon_vma_chain 來聯系對應的 vma 和 av),當然這個關系建立比較復雜,涉及到 vma,avc 和 av 這些數據結構體。.
而在缺頁異常 do_anonymous_page 的時候將 page 和 vma 相關聯。
當內存回收或頁面遷移的時候,內核路徑最終會調用到:
try_to_unmap //mm/rmap.c - rmap_walk - rmap_walk_anon - anon_vma_interval_tree_foreach(avc, anon_vma- rb_root,pgoff_start, pgoff_end) - rwc- rmap_one - try_to_unmap_one
對于候選頁,會拿到候選頁相關聯的 anon_vma,然后從 anon_vma 的紅黑樹中遍歷到所有共享這個頁的 vma,然后對于每一個 vma 通過 try_to_unmap_one 來處理相對應的頁表項,將映射關系解除。
4. 文件頁的反向映射
文件頁的共享主要發生在多個進程共享 libc 庫,同一個庫文件可以只需要讀取到 page cache 一次,然后通過各個進程的頁表映射到各個進程的 vma 中。
管理共享文件頁的所以 vma 是通過 address_space 的區間樹來管理,在 mmap 或者 fork 的時候將 vma 加入到這顆區間樹中:
發生文件映射缺頁異常的時候,將 page 和 address_space 相關聯。
當內存回收或頁面遷移的時候,內核路徑最終會調用到:
try_to_unmap //mm/rmap.c - rmap_walk - rmap_walk_file - vma_interval_tree_foreach(vma, mapping i_mmap,pgoff_start, pgoff_end) - rwc- rmap_one
對于每一個候選的文件頁,如果是映射頁,就會遍歷 page 所對應的 address_space 的區間樹,對于每一個滿足條件的 vma,調用 try_to_unmap_one 來找到 pte 并解除映射關系。
5.ksm 頁的反向映射
ksm 機制是內核將頁面內容完全相同的頁面進行合并 (ksm 管理的都是匿名頁),將映射到這個頁面的頁表項標記為只讀,然后釋放掉原來的頁表,來達到節省大量內存的目的,這對于 host 中開多個虛擬機的應用場景非常有用。
ksm 機制中會管理兩課紅黑樹,一棵是 stable tree, 一棵是 unstable tree,stable tree 中的每個節點 stable_node 中管理的頁面都是頁面內容完全相同的頁面 (被叫做 kpage), 共享 kpage 的頁面的頁表項都會標記為只讀,而且對于原來的候選頁都會有 rmap_item 來描述他的反向映射 (其中的 anon_vma 成員的紅黑樹是描述映射這個候選頁的所有 vma 的集合),合并的時候會加入到對應的 stable tree 節點和鏈表中。
當內存回收或頁面遷移的時候,內核路徑最終會調用到:
try_to_unmap //mm/rmap.c - rmap_walk - rmap_walk_ksm //mm/ksm.c - hlist_for_each_entry(rmap_item, stable_node- hlist, hlist) - anon_vma_interval_tree_foreach(vmac, anon_vma- rb_root,0, ULONG_MAX) - rwc- rmap_one
對于一個 ksm 頁面,反向映射的時候,會拿到 ksm 頁面對應的節點,然后遍歷節點的 hlist 鏈表,拿到每一個 anon_vma,然后就和上面介紹的匿名頁的反向映射一樣了,從 anon_vma 的紅黑樹中找到所有的 vma,最后 try_to_unmap_one 來找到 pte 并解除映射關系。
到此,相信大家對“Linux 內核反向映射機制是什么”有了更深的了解,不妨來實際操作一番吧!這里是丸趣 TV 網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!