當前位置:
首頁 > 知識 > Linux內核頁面換入換出

Linux內核頁面換入換出

0x00內存頁面分類與換入換出規則

內存頁面分為用戶頁面和內核頁面。

用戶頁面有以下幾種:

1、普通的用戶空間頁面,包括進程的代碼段、數據段、堆棧段、以及動態分配的存儲堆。

2、通過系統調用mmap()映射到用戶空間的已打開文件的內容。

這些頁面既涉及分配、使用和回收,也涉及頁面的換出/換入。

內核頁面有以下幾種:

1、kmalloc分配用作某些臨時性的數據結構,如vma_area_struct。

2、內核通過alloc_page分配,如每個進程的系統堆棧所在的兩個頁面。

這些頁面不涉及頁面的換出/換入,一旦使用完畢,就可以釋放、回收。

3、文件系統相關的結構體入dentry、node

這些頁面不涉及頁面的換出/換入,但即使使用完畢,其內容扔有保存的價值,只要條件允許,就將這些頁面養起來,可以提高以後的操作效率

4、內核代碼和內核中全局量所佔的內存頁面

這些頁面既不需要分配,也不會被釋放

0x01用戶頁面的換入

對於內核來說,只有兩種用戶頁面,一種是文件映射,一種是匿名映射。前一種和swap沒有關係,直接換出到硬碟上文件。後者會交換到swap。

1、文件映射--->換出到硬碟

2、匿名映射--->換出到swap

既然涉及到換出,我們還是先說下換入,換入也由換出定義的這兩種頁面為導向。

1、可執行文件(文件映射還包含直接映射硬碟上某個文件,不限於可執行文件)的換入,可以參考load_elf_binary,http://elixir.free-electrons.com/linux/v2.6.39.4/source/fs/binfmt_elf.c#L559

[cpp] view plain copy print?

  1. error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt,

  2. elf_prot, elf_flags, 0);

error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt,

elf_map會進一步調用mmap將可執行文件的映射到內存,這種映射屬於文件映射。

我們先看下映射到內存虛擬地址的情況:

Linux內核頁面換入換出

0x00601000-0x00602000這個虛擬地址空間映射為可寫,我們再來看看這個區域到底放著什麼信息?

Linux內核頁面換入換出

裡面是.data段、.bss段一些可寫的段。

那麼就涉及到一個問題,既然是文件映射,當發生頁面短缺時,是要換出到硬碟的。我們可以想像下總不可能每執行一次文件,我硬碟的可執行文件的數據就變化一下吧?那麼Linux內核是怎麼處理的呢?我們放在後面再說。

首先理清一個思路,mmap這個系統調用,只是申請一片虛擬內存地址,並沒有實際到把硬碟中的數據讀到內存,並建立映射(建立頁目錄表、頁表)。

當訪問到對應的虛擬地址空間時,觸發缺頁中斷do_no_page,從硬碟中把數據讀到內存,並建立映射。

[cpp] view plain copy print?


  1. if (!pte_present(entry)) {//頁面不在內存中

  2. /*

  3. * If it truly wasn"t present, we know that kswapd

  4. * and the PTE updates will not touch it later. So

  5. * drop the lock.

  6. */

  7. spin_unlock(&mm->page_table_lock);

  8. if (pte_none(entry))//頁表項為空

  9. return do_no_page(mm, vma, address, write_access, pte);

  10. return do_swap_page(mm, vma, address, pte, pte_to_swp_entry(entry), write_access);//執行到這裡

  11. }

if (!pte_present(entry)) {//頁面不在內存中

[cpp] view plain copy print?

  1. static int do_no_page(struct mm_struct * mm, struct vm_area_struct * vma,

  2. unsigned long address, int write_access, pte_t *page_table)

  3. {

  4. struct page * new_page;

  5. pte_t entry;

  6. if (!vma->vm_ops || !vma->vm_ops->nopage)

  7. return do_anonymous_page(mm, vma, page_table, write_access, address);

  8. /*

  9. * The third argument is "no_share", which tells the low-level code

  10. * to copy, not share the page even if sharing is possible. It"s

  11. * essentially an early COW detection.

  12. */

  13. new_page = vma->vm_ops->nopage(vma, address & PAGE_MASK, (vma->vm_flags & VM_SHARED)?0:write_access);//指向了filemap_nopage

  14. if (new_page == NULL) /* no page was available -- SIGBUS */

  15. return 0;

  16. if (new_page == NOPAGE_OOM)

  17. return -1;

  18. ++mm->rss;

  19. /*

  20. * This silly early PAGE_DIRTY setting removes a race

  21. * due to the bad i386 page protection. But it"s valid

  22. * for other architectures too.

  23. *

  24. * Note that if write_access is true, we either now have

  25. * an exclusive copy of the page, or this is a shared mapping,

  26. * so we can make it writable and dirty to avoid having to

  27. * handle that later.

  28. */

  29. flush_page_to_ram(new_page);

  30. flush_icache_page(vma, new_page);

  31. entry = mk_pte(new_page, vma->vm_page_prot);

  32. if (write_access) {

  33. entry = pte_mkwrite(pte_mkdirty(entry));

  34. } else if (page_count(new_page) > 1 &&

  35. !(vma->vm_flags & VM_SHARED))

  36. entry = pte_wrprotect(entry);

  37. set_pte(page_table, entry);//建立映射

  38. /* no need to invalidate: a not-present page shouldn"t be cached */

  39. update_mmu_cache(vma, address, entry);

  40. return 2; /* Major fault */

  41. }

static int do_no_page(struct mm_struct * mm, struct vm_area_struct * vma,

此時分配的頁面被放入了活躍隊列active_list。

2、匿名內存換入

匿名內存通常是堆棧和存儲堆,存儲堆通過malloc分配,malloc底層調用mmap,不過傳入的參數fd為-1,表示匿名映射

void *mmap(void *addr, size_t length, int prot, int flags,

匿名映射當發生缺頁中斷時,也會調用do_no_page,然後調用do_anonymous_page,分配內頁,建立映射,加入了活躍隊列active_list。

0x02用戶頁面的換出

1、思考幾個問題:

頁面是直接換出到swap或者硬碟么?

這樣的話,如果剛被換出,又立刻訪問了呢?就需要再一次換入到內存,從而造成內存抖動。linux內核的處理如下:

linux內核有兩個內核線程,專門用於換出頁面,他們是kswapd和kreclaimd。詳情請參考Linux內核源代碼情景分析-內存管理之用戶頁面的定期換出。

1)kswapd線程:

當檢測是頁面短缺時,根據某些規則挑選一些頁面,調用refill_inactive_scan和swap_out,把活躍的頁面變成不活躍髒的頁面。從active_list移除,並加入inactive_dirty隊列;

之後還會調用page_launder,把不活躍髒的頁面變成不活躍乾淨的頁面,從inactive_dirty隊列移除,並加入到inactive_clean隊列。

2)kreclaimd內核線程:

把不活躍乾淨的頁面,所有的鏈表關係都清除,但使用計數仍然為1。

__free_page,此時使用計數減為0,回收這個頁面到free_area[MAX_ORDER],下次alloc_page就能分配到了。

2、針對被移到inactive_dirty隊列中的page,此時頁面中內容和磁碟或者swap分區中的內容是一致的,現在的頁表項分為兩種情況:

1)針對匿名映射的頁面,此時頁表項指向一個新分配的swap分區地址,用於將來換出到 swap分區

2)針對文件映射的頁面,此時頁表項已經清空,換出到硬碟有專門的函數處理。

0x03用戶頁面的再次換入

當下一次訪問到對應的頁面時,由於頁表項已經清空或者指向swap分區(但是最後一位為0,依然會觸發缺頁中斷)。

對於匿名映射的頁面,缺頁中斷會調用do_swap_page;

對於文件映射的頁面,缺頁中斷會調用do_no_page;

此時頁面並沒有從hash列表移除,所以兩者可以從hash表中讀到對應的頁面,直接建立映射即可,不需要重新換入,減少內存抖動。

[cpp] view plain copy print?

  1. static int do_swap_page(struct mm_struct * mm,

  2. struct vm_area_struct * vma, unsigned long address,

  3. pte_t * page_table, swp_entry_t entry, int write_access)

  4. {

  5. struct page *page = lookup_swap_cache(entry);//從hash表中尋找

  6. pte_t pte;

  7. if (!page) {

  8. lock_kernel();

  9. swapin_readahead(entry);//預讀頁面

  10. page = read_swap_cache(entry);//真正得到一個頁面,這個頁面可能從hash表中尋找到,因為上面預讀了。或者自己申請頁面,並且從盤上將其內容讀進來。

static int do_swap_page(struct mm_struct * mm,

PS: 雖然已經建立了映射,但是page所在的隊列,應該被移到active_list中,什麼時候移到的呢?請參考http://blog.csdn.NET/jltxgcy/article/details/44055485

2、針對kreclaimd內核線程處理後的頁面,由於已經從hash列表中移除,所以此時如果發生缺頁中斷,就要真刀真槍的從swap分區或者硬碟中讀入數據。

對於匿名頁面映射,此時頁表項已經指向了swap分區,頁面從swap分區換入,並建立映射,重新放入active_list。

對於文件頁面映射,此時頁表項為空,頁面從硬碟分區換入,並建立,重新放入active_list。

0x04回答最開始的問題

裡面是.data段、.bss段一些可寫的段。

那麼就涉及到一個問題,既然是文件映射,當發生頁面短缺時,是要換出到硬碟的。我們可以想像下總不可能每執行一次文件,我硬碟的可執行文件的數據就變化一下吧?那麼linux內核是怎麼處理的呢?

在載入可執行文件時,有這段代碼:

[cpp] view plain copy print?

  1. elf_flags = MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE;

  2. vaddr = elf_ppnt->p_vaddr;

  3. if (loc->elf_ex.e_type == ET_EXEC || load_addr_set) {

  4. elf_flags |= MAP_FIXED;

  5. } else if (loc->elf_ex.e_type == ET_DYN) {

  6. /* Try and get dynamic programs out of the way of the

  7. * default mmap base, as well as whatever program they

  8. * might try to exec. This is because the brk will

  9. * follow the loader, and is not movable. */

  10. #if defined(CONFIG_X86) || defined(CONFIG_ARM)

  11. load_bias = 0;

  12. #else

  13. load_bias = ELF_PAGESTART(ELF_ET_DYN_BASE - vaddr);

  14. #endif

  15. }

  16. error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt,

  17. elf_prot, elf_flags, 0);

elf_flags = MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE;

傳遞給mmap的flag中有MAP_PRIVATE,下面是對MAP_PRIVATE的解釋。

MAP_PRIVATE

linxu內核也有這段描述,documentation/filesystems/proc.txt:

"Anonymous" shows the amount of memory that does not belong to any file. Even a mapping associated with a file may contain anonymous pages: when MAP_PRIVATE and a page is modified, the file page is replaced by a private anonymous copy.

也就是說上面那段區域當寫入時,會執行copy_on_write創建一份匿名映射,然後可以被換出到swap分區。我們在圖中看到三個segment是不會被換出的硬碟的,我們已經聲明了MAP_DENYWRITE。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 青峰科技 的精彩文章:

C++實現稀疏矩陣的壓縮存儲
Linux系統CPU的性能監控及調優
DLL注入技術
SQL Server 2017中新的T-SQL函數
深入理解 Android 控制項

TAG:青峰科技 |

您可能感興趣

蘋果官方推出舊機換iPhone XR:看了價格默默關閉頁面
GitHub 改版,重構頁面移除了 jQuery?
內地搶購頁面已經釋出!Virgil Abloh x Nike Blazer Mid明日抽籤!
蘋果從Facebook iTunes頁面上刪除了社交媒體內容
iPhone 9、iPhone XS及Max的預購頁面
怎麼打造LinkedIn展示頁面?七大秘訣打造完美LinkedIn展示頁面
Ubuntu 改滑鼠側鍵為ctrl,並配合滾輪實現頁面縮放
Windows、Linux等系統遭遇頁面緩存旁路攻擊
router.push(傳參)跳轉頁面,參數改變,跳轉頁面數據不刷新
vue Router在新標籤打開頁面的實踐
Selenium及Headless Chrome抓取動態HTML頁面
jQuery Mobile 頁面
Servlet 自動刷新頁面
Facebook全面實施GDPR 用戶Pages頁面被隨意鎖定
Chrome Canary的設置頁面布局 被調整得更加易於使用
YouTube宣布多項頁面調整措施
CoinMarketCap悄悄地從BTC頁面中刪除Bitcoin.com
Pixel Sounds更新 用戶頁面更加多彩活力
Intellij idea集成的git頁面進行操作
Kodak Alaris完美頁面技術:省去文檔預分類 輸出完美掃描圖像