久综合色-久综合网-玖草影视-玖草资源在线-亚洲黄色片子-亚洲黄色片在线观看

Hello! 歡迎來到小浪云!


深入理解Linux內(nèi)核之mmu-gather操作


avatar
小浪云 2025-01-06 160

linux內(nèi)核的虛擬內(nèi)存管理中,特別是在處理器架構(gòu)arm64、內(nèi)核源碼版本為linux-5.10.50、運行的Ubuntu版本為20.04.1,并借助于代碼閱讀工具vim、ctags以及cscope的情況下,我們將深入探討mmu_gather操作的機制。我們將看到這個操作是如何確保在釋放物理頁面之前正確地刷新tlb,并且如何聚集更多的頁面以便統(tǒng)一釋放。

當(dāng)一個進程退出或者執(zhí)行munmap操作時,內(nèi)核需要解除相關(guān)虛擬內(nèi)存區(qū)域的頁表映射,并且在刷新或者無效化tlb后釋放或者回收相關(guān)的物理頁面。這個過程的正確順序至關(guān)重要,按照如下步驟進行:

  1. 解除頁表映射。
  2. 刷新相關(guān)的tlb。
  3. 釋放物理頁面。

在刷新相關(guān)虛擬內(nèi)存區(qū)域的tlb之前,絕對不能提前釋放物理頁面,否則可能會導(dǎo)致不正確的結(jié)果。這就是mmu-gather(mmu積聚)操作的作用所在,它確保了這種順序,并且將需要釋放的相關(guān)物理頁面聚集在一起以便統(tǒng)一釋放。

2.源代碼解讀

2.1 重要數(shù)據(jù)結(jié)構(gòu)

首先我們先介紹一下,與mmu-gather相關(guān)的一些重要結(jié)構(gòu)體,對于理解源碼很有幫助。

相關(guān)的主要數(shù)據(jù)結(jié)構(gòu)有三個:

struct mmu_gather

Struct mmu_table_batch

struct mmu_gather_batch

1)mmu_gather

來表示一次mmu積聚操作,在每次解除相關(guān)虛擬內(nèi)存區(qū)域時使用。

struct?mmu_gather?{ ????????struct?mm_struct????????*mm;  #ifdef?CONFIG_MMU_GATHER_TABLE_FREE ????????struct?mmu_table_batch??*batch; #endif  ????????unsigned?long???????????start; ????????unsigned?long???????????end; ????????/* ????????|*?we?are?in?the?middle?of?an?operation?to?clear ????????|*?a?full?mm?and?can?make?some?optimizations ????????|*/ ????????unsigned?int????????????fullmm?:?1;  ????????/* ????????|*?we?have?performed?an?operation?which ????????|*?requires?a?complete?flush?of?the?tlb ????????|*/ ????????unsigned?int????????????need_flush_all?:?1;  ????????/* ????????|*?we?have?removed?page?directories ????????|*/ ????????unsigned?int????????????freed_tables?:?1;  ????????/* ????????|*?at?which?levels?have?we?cleared?entries? ????????|*/ ????????unsigned?int????????????cleared_ptes?:?1; ????????unsigned?int????????????cleared_pmds?:?1; ????????unsigned?int????????????cleared_puds?:?1; ????????unsigned?int????????????cleared_p4ds?:?1;  ????????/* ????????|*?tracks?VM_EXEC?|?VM_HUGETLB?in?tlb_start_vma ????????|*/ ????????unsigned?int????????????vma_exec?:?1; ????????unsigned?int????????????vma_huge?:?1; ????????????????????????????????????????????????????????????????????????? ????????unsigned?int????????????batch_count;??????????? ????????#ifndef?CONFIG_MMU_GATHER_NO_GATHER??????????????????????????? ????????struct?mmu_gather_batch?*active;?????????????????????? ????????struct?mmu_gather_batch?local;???????????????????????? ????????struct?page?????????????*__pages[MMU_GATHER_BUNDLE];? ??????????????????????????...???????????????????????????????????????????????????? ????????#endif ?????????????????????????? };??????????????????????????????????????????????????????????????????????? 

其中, mm 表示操作哪個進程的虛擬內(nèi)存;batch 用于積聚進程各級頁目錄的物理頁;start和end 表示操作的起始和結(jié)束虛擬地址,這兩個地址在處理過程中會被相應(yīng)的賦值;fullmm 表示是否操作整個用戶地址空間;freed_tables 表示我們已經(jīng)釋放了相關(guān)的頁目錄;cleared_ptes/pmds/puds/p4ds 表示我們在哪個級別上清除了表項;vma_exec 表示操作的是否為可執(zhí)行的vma;vma_huge 表示操作的是否為hugetlb的vma;batch_count 表示積聚了多少個“批次”,后面會講到 ;active、local和__pages 和多批次釋放物理頁面相關(guān);active表示當(dāng)前處理的批次,local表示“本地”批次,__pages表示“本地”批次積聚的物理頁面。

這里需要說明一點就是,mmu積聚操作會涉及到local批次和多批次操作,local批次操作的物理頁面相關(guān)的struct page數(shù)組內(nèi)嵌到mmu_gather結(jié)構(gòu)的__pages中,且我們發(fā)現(xiàn)這個數(shù)組大小為8,也就是local批次最大積聚8 * 4k = 32k的內(nèi)存大小,這因為mmu_gather結(jié)構(gòu)通常在內(nèi)核中分配,不能占用太多的內(nèi)核空間,而多批次由于動態(tài)分配批次積聚結(jié)構(gòu)所以每個批次能積聚更多的頁面。

2)mmu_table_batch

用于積聚進程使用的各級頁目錄的物理頁,在釋放進程相關(guān)的頁目錄的物理頁時使用(文章中稱為頁表批次的積聚結(jié)構(gòu))。

struct?mmu_table_batch?{ #ifdef?CONFIG_MMU_GATHER_RCU_TABLE_FREE ????????struct?rcu_head?????????rcu;???????????????? #endif?? ????????unsigned?int????????????nr;????????????????? ????????void????????????????????*tables[0];????????? }; 

rcu 用于rcu延遲釋放頁目錄的物理頁;

nr 表示頁目錄的物理頁的積聚結(jié)構(gòu)的page數(shù)組中頁面?zhèn)€數(shù);

tables 表示頁表積聚結(jié)構(gòu)的page數(shù)組。

3)mmu_gather_batch

表示物理頁的積聚批次,用于積聚進程映射到用戶空間物理頁(文章中稱為批次的積聚結(jié)構(gòu))。

?struct?mmu_gather_batch?{?????? ?????????????????? ?????????struct?mmu_gather_batch?*next; ?????????unsigned?int????????????nr;???? ?????????? ?????????unsigned?int????????????max;?? ??????????? ?????????struct?page?????????????*pages[0];???? ??? ?};?????? 

next 用于多批次積聚物理頁時,連接下一個積聚批次結(jié)構(gòu) ;

nr 表示本次批次的積聚數(shù)組的頁面?zhèn)€數(shù);

max 表示本次批次的積聚數(shù)組最大的頁面?zhèn)€數(shù);

pages 表示本次批次積聚結(jié)構(gòu)的page數(shù)組。

2.2 總體調(diào)用

通常mmu-gather操作由一下幾部分函數(shù)組成:

tlb_gather_mmu

unmap_vmas

free_pgtables

tlb_finish_mmu

其中tlb_gather_mmu表示mmu-gather初始化,也就是struct mmu_gather的初始化;

unmap_vmas 表示解除相關(guān)虛擬內(nèi)存區(qū)域的頁表映射;

free_pgtables 表示釋放頁表操作 ;

tlb_finish_mmu 表示進行刷tlb和釋放物理頁操作。

2.3 tlb_gather_mmu

這個函數(shù)主要是初始化從進程內(nèi)核棧中傳遞過來的mmu_gather結(jié)構(gòu)。

void?tlb_gather_mmu(struct?mmu_gather?*tlb,?struct?mm_struct?*mm,??????????? ????? ????????????????????????unsigned?long?start,?unsigned?long?end)?????????????????? {???????????????????????????????????????????????????????????????????????????????? ????????tlb->mm?=?mm;??????????//賦值進程的內(nèi)存描述符?????????????????????????????????????????????????? ????????????????????????????????????????????????????????????????????????????????? ????????/*?Is?it?from?0?to?~0??*/??????????????????????????????????? ????????????? ????????tlb->fullmm?????=?!(start?|?(end+1));????//如果是操作進程整個地址空間,  則?start=0,???end=-1,這個時候?fullmm會被賦值1??????????????????????? ????????????????????????????????????????????????????????????????????????????????? #ifndef?CONFIG_MMU_GATHER_NO_GATHER?????????????????????????????????????????????? ????????tlb->need_flush_all?=?0;??????????????????//初始化“本地”積聚相關(guān)成員??????????????????????????????? ????????tlb->local.next?=?NULL;?????????????????????????????????????????????????? ????????tlb->local.nr???=?0;????????????????????????????????????????????????????? ????????tlb->local.max??=?ARRAY_SIZE(tlb->__pages);?????????????????????????????? ????????tlb->active?????=?&tlb->local;????//active指向本地的積聚結(jié)構(gòu)???????????????????????????????????????? ????????tlb->batch_count?=?0;???????????????????????????????????????????????????? #endif??????????????????????????????????????????????????????????????????????????? ????????????????????????????????????????????????????????????????????????????????? ????????tlb_table_init(tlb);??????????//?????tlb->batch?=?NULL???,來表示先不使用多批次積聚??????????????????????????????????? #ifdef?CONFIG_MMU_GATHER_PAGE_SIZE??????????????????????????????????????????????? ????????tlb->page_size?=?0;?????????????????????????????????????????????????????? #endif??????????????????????????????????????????????????????????????????????????? ????????????????????????????????????????????????????????????????????????????????? ????????__tlb_reset_range(tlb);??????//tlb->start/end?和??lb->freed_tables、tlb->cleared_xxx初始化???????????????????????????????????????? ????????inc_tlb_flush_pending(tlb->mm);?????????????????????????????????????????? }???????????????????????????????????????????????????????????????????????????????? 

下面給出tlb_gather_mmu時的圖解:

深入理解Linux內(nèi)核之mmu-gather操作

2.4 unmap_vmas

這個函數(shù)用于解除相關(guān)進程虛擬內(nèi)存區(qū)域的頁表映射,還會將相關(guān)的物理頁面放入積聚結(jié)構(gòu)中,后面統(tǒng)一釋放。

下面我們來看下這個函數(shù):

void?unmap_vmas(struct?mmu_gather?*tlb, ????????????????struct?vm_area_struct?*vma,?unsigned?long?start_addr, ????????????????unsigned?long?end_addr) { ????????... ????????for?(?;?vma?&&?vma->vm_start?vm_next) ????????????????unmap_single_vma(tlb,?vma,?start_addr,?end_addr,?NULL); ????????... } 

函數(shù)傳遞進已經(jīng)初始化好的mmu積聚結(jié)構(gòu)、操作的起始vma、以及虛擬內(nèi)存范圍[start_addr, end_addr], 然后調(diào)用unmap_single_vma來操作這個范圍內(nèi)的每一個vma。

unmap_single_vma的實現(xiàn)相關(guān)代碼比較多,在此不在贅述,我們會分析關(guān)鍵代碼,它主要做的工作為:通過遍歷進程的多級頁表,來找到vma中每一個虛擬頁對應(yīng)的物理頁(存在的話),然后解除虛擬頁到物理頁的映射關(guān)系,最后將物理頁放入積聚結(jié)構(gòu)中。

總體調(diào)用如下:

//?mm/memory.c unmap_vmas ->?unmap_single_vma?//處理單個vma ????->??unmap_page_range ????????->zap_p4d_range?//遍歷pge頁目錄中每一個p4d表項 ????????????->zap_pud_range??//遍歷p4d頁目錄中每一個pud表項 ????????????????->zap_pmd_range?//遍歷pud頁目錄中每一個pmd表項 ????????????????????->zap_pte_range??//遍歷pmd頁目錄中每一個pmd表項 

下面我們省略中間各級頁表的遍歷過程,重點看下最后一級頁表的處理(這段代碼相當(dāng)關(guān)****鍵):

zap_pte_range ->  static?unsigned?long?zap_pte_range(struct?mmu_gather?*tlb, ????????????????????????????????struct?vm_area_struct?*vma,?pmd_t?*pmd, ????????????????????????????????unsigned?long?addr,?unsigned?long?end, ????????????????????????????????struct?zap_details?*details) { ????????????... again: ????????init_rss_vec(rss); ????????start_pte?=?pte_offset_map_lock(mm,?pmd,?addr,?&ptl);????//根據(jù)addr從pmd指向的頁表中獲得頁表項指針,  并申請頁表的自旋鎖???????????????????????????????????????? ????????pte?=?start_pte; ????????flush_tlb_batched_pending(mm); ????????arch_enter_lazy_mmu_mode(); ????????do?{ ????????????????pte_t?ptent?=?*pte;???//獲得頁表項??????????????????????????????????????????????????????????????????? ????????????????if?(pte_none(ptent))??//頁表項的內(nèi)容?為空表示沒有映射過,繼續(xù)下一個虛擬頁 ????????????????????????continue;  ?????????????????... ???????????????? ????????????????if?(pte_present(ptent))?{??//虛擬頁相關(guān)的物理頁在內(nèi)存中(如沒有被換出到swap) ????????????????????????struct?page?*page;  ????????????????????????page?=?vm_normal_page(vma,?addr,?ptent);????//獲得虛擬頁相關(guān)的物理頁?????????????????????????????????????? ????????????????????????... ???????????????????????? ?????????????????????????ptent?=?ptep_get_and_clear_full(mm,?addr,?pte,???????????????????????????? ?????????????????????????????????????????????????????????tlb->fullmm);??//將頁表項清空(即是解除了映射關(guān)  系),并返回原來的頁表項的內(nèi)容?????????????????????????? ?????????????????????????tlb_remove_tlb_entry(tlb,?pte,?addr);????????????????????????????????????? ?????????????????????????if?(unlikely(!page))?????????????????????????????????????????????????????? ?????????????????????????????????continue;????????????????????????????????????????????????????????? ??????????????????????????????????????????????????????????????????????????????????????????????????? ?????????????????????????if?(!PageAnon(page))?{????//如果是文件頁???????????????????????????????????????????????? ?????????????????????????????????if?(pte_dirty(ptent))?{??//是臟頁????????????????????????????????????????? ?????????????????????????????????????????force_flush?=?1;?????//強制刷tlb????????????????????????????????????? ?????????????????????????????????????????set_page_dirty(page);??//臟標(biāo)志傳遞到page結(jié)構(gòu)??????????????????????????????????? ?????????????????????????????????}????????????????????????????????????????????????????????????????? ?????????????????????????????????if?(pte_young(ptent)?&&??????????????????????????????????????????? ?????????????????????????????????|???likely(!(vma->vm_flags?&?VM_SEQ_READ)))???????//如果頁表項訪問標(biāo)志置  位,且是隨機訪問的vma,則標(biāo)記頁面被訪問???????????????? ?????????????????????????????????????????mark_page_accessed(page);????????????????????????????????? ?????????????????????????}????????????????????????????????????????????????????????????????????????? ?????????????????????????rss[mm_counter(page)]--;??//進程的相關(guān)rss?做減1記賬???????????????????????????????????????????????? ?????????????????????????page_remove_rmap(page,?false);???//??page->_mapcount-- ?????????????????????????if?(unlikely(page_mapcount(page)?if?(unlikely(__tlb_remove_page(tlb,?page)))?{??//將物理頁記錄到積聚結(jié)構(gòu)中,???如果  分配不到mmu_gather_batch結(jié)構(gòu)或不支持返回true???????????????????????? ?????????????????????????????????force_flush?=?1;?????//強制刷tlb?????????????????????????????????????????????  ??????????????????????????????addr?+=?PAGE_SIZE;?????//操作下一個虛擬頁??????????????????????????? ??????????????????????????????break;?????//退出循環(huán)?????????????????????????????????????????????????? ??????????????????????}???????????????????????????????????????????????????????????????????? ??????????????????????continue;???//正常情況下,處理下一個虛擬頁????????????????????????????????????????????????????????? ??????????????}???????????????????????????????????????????????????????????????????????????? ???????????????????????????????????????? ???????????????//下面處理虛擬頁相關(guān)的物理頁“不在”內(nèi)存中的情況,可能是交換到swap或者是遷移類型等  ??????????????entry?=?pte_to_swp_entry(ptent);???//頁表項得到??swp_entry???????????????????????????????????????? ??????????????if?(is_device_private_entry(entry))?{???//處理設(shè)備內(nèi)存表項????????????????????????????????????? ??????????????????????struct?page?*page?=?device_private_entry_to_page(entry);????????????? ??????????????????????????????????????????????????????????????????????????????????????????? ??????????????????????if?(unlikely(details?&&?details->check_mapping))?{??????????????????? ??????????????????????????????/*??????????????????????????????????????????????????????????? ??????????????????????????????|*?unmap_shared_mapping_pages()?wants?to????????????????????? ??????????????????????????????|*?invalidate?cache?without?truncating:?????????????????????? ??????????????????????????????|*?unmap?shared?but?keep?private?pages.?????????????????????? ??????????????????????????????|*/?????????????????????????????????????????????????????????? ??????????????????????????????if?(details->check_mapping?!=???????????????????????????????? ??????????????????????????????|???page_rmapping(page))????????????????????????????????????? ??????????????????????????????????????continue;???????????????????????????????????????????? ??????????????????????}???????????????????????????????????????????????????????????????????? ??????????????????????????????????????????????????????????????????????????????????????????? ??????????????????????pte_clear_not_present_full(mm,?addr,?pte,?tlb->fullmm);?????????????? ??????????????????????rss[mm_counter(page)]--;????????????????????????????????????????????? ??????????????????????page_remove_rmap(page,?false);??????????????????????????????????????? ??????????????????????put_page(page);?????????????????????????????????????????????????????? ??????????????????????continue;???????????????????????????????????????????????????????????? ??????????????}???????????????????????????????????????????????????????????????????????????? ??????????????????????????????????????????????????????????????????????????????????????????? ????????????????????????????????????????????????????????????????????????????????????? ???????????????....????????????????????????????????????? ????????????????????????????????????????????????????????????????????????????????????? ????????????????if?(!non_swap_entry(entry))??//非遷移類型的swap_entry???????????????????????????????? ????????????????????????rss[MM_SWAPENTS]--;??????//進程相關(guān)的交換條目的rss?減1?????????????????????????????????????? ????????????????else?if?(is_migration_entry(entry))?{???//遷移類型的表項?????????????????????????????? ????????????????????????struct?page?*page;??????????????????????????????????????????? ????????????????????????????????????????????????????????????????????????????????????? ????????????????????????page?=?migration_entry_to_page(entry);?????????//得到對應(yīng)的物理頁?????????????? ????????????????????????rss[mm_counter(page)]--;?????//進程相關(guān)的物理頁類型的rss?減1 ????????????????}???????????????????????????????????????????????????????????????????? ????????????????if?(unlikely(!free_swap_and_cache(entry)))???//釋放swap條目 ????????????????????????print_bad_pte(vma,?addr,?ptent,?NULL);??????????????????????? ????????????????pte_clear_not_present_full(mm,?addr,?pte,?tlb->fullmm);?????//清除虛擬頁相關(guān)的物理頁的頁表映  射?????????? ????????}?while?(pte++,?addr?+=?PAGE_SIZE,?addr?!=?end);?????//遍歷pmd表項管轄范圍內(nèi)的每一個虛擬頁???????????????????????? ????????????????????????????????????????????????????????????????????????????????????? ????????add_mm_rss_vec(mm,?rss);????//記錄到進程的相關(guān)rss結(jié)構(gòu)中????????????????????????????????????????????????? ????????arch_leave_lazy_mmu_mode();?????????????????????????????????????????????????? ????????????????????????????????????????????????????????????????????????????????????? ????????/*?Do?the?actual?TLB?flush?before?dropping?ptl?*/???????????????????????????? ????????if?(force_flush)????????????????????????????????????????????????????????????? ????????????????tlb_flush_mmu_tlbonly(tlb);???//如果是強制刷tlb,則刷tlb??????????????????????????????????????? ????????pte_unmap_unlock(start_pte,?ptl);?????//釋放進程的頁表自旋鎖??????????????????????????????????????? ????????????????????????????????????????????????????????????????????????????????????? ??????????????????????????????????????????????????????????????????????????????????????????? ???????/*?????????????????????????????????????????????????????????????????????????????????? ???????|*?If?we?forced?a?TLB?flush?(either?due?to?running?out?of??????????????????????????? ???????|*?batch?buffers?or?because?we?needed?to?flush?dirty?TLB???????????????????????????? ???????|*?entries?before?releasing?the?ptl),?free?the?batched?????????????????????????????? ???????|*?memory?too.?Restart?if?we?didn't?do?everything.?????????????????????????????????? ???????|*/????????????????????????????????????????????????????????????????????????????????? ???????if?(force_flush)?{????????//如果是強制刷tlb,則釋放掉本次聚集的物理頁???????????????????????????????????????????????? ???????????????force_flush?=?0;???????????????????????????????????????????????????????????? ???????????????tlb_flush_mmu(tlb);???//釋放本次聚集的物理頁????????????????????????????????????????????????????????? ???????}??????????????????????????????????????????????????????????????????????????????????? ??????????????????????????????????????????????????????????????????????????????????????????? ???...????????????????????????????????????????????????????? ??????????????????????????????????????????????????????????????????????????????????????????? ???????return?addr;?????????? ??????? ??} ??????????????????????????????????????????????????????????????????????????????????????????? 

以上函數(shù),遍歷進程相關(guān)頁表(一個pmd表項指向一個頁表)所描述的范圍的每一個虛擬頁,如果之前已經(jīng)建立過映射,就將相關(guān)的頁表項清除,對于在內(nèi)存中物理頁來說,需要調(diào)用__tlb_remove_page將其加入到mmu的積聚結(jié)構(gòu)中,下面重點看下這個函數(shù):

__tlb_remove_page ->__tlb_remove_page_size??//mm/mmu_gather.c ????-> ????bool?__tlb_remove_page_size(struct?mmu_gather?*tlb,?struct?page?*page,?int?page_size)?????????????? {?????????????????????????????????????????????????????????????????????????????????????????????????? ????????struct?mmu_gather_batch?*batch;???????????????????????????????????????????????????????????? ??????????????????????????????????????????????????????????????????????????????????????????????????? ????????...??????????????????????????? ??????????????????????????????????????????????????????????????????????????????????????????????????? ????????batch?=?tlb->active;??//獲得當(dāng)前批次的積聚結(jié)構(gòu)???????????????????????????????????????????????????????????????????? ????????/*????????????????????????????????????????????????????????????????????????????????????????? ????????|*?Add?the?page?and?check?if?we?are?full.?If?so???????????????????????????????????????????? ????????|*?force?a?flush.?????????????????????????????????????????????????????????????????????????? ????????|*/???????????????????????????????????????????????????????????????????????????????????????? ????????batch->pages[batch->nr++]?=?page;??//將頁面加入到???批次的積聚結(jié)構(gòu)的pages數(shù)組中,并增加?batch->nr計數(shù)???????????????????????????????????????????????????? ????????if?(batch->nr?==?batch->max)?{??//如果當(dāng)前批次的?積聚結(jié)構(gòu)的pages數(shù)組中積聚的頁面?zhèn)€數(shù)到達最大個數(shù)?????????????????????????????????????????????????????????? ????????????????if?(!tlb_next_batch(tlb))??//獲得下一個批次積聚結(jié)構(gòu)???????????????????????????????????????????????????????? ????????????????????????return?true;????//獲得不成功返回true??????????????????????????????????????????????????????????? ????????????????batch?=?tlb->active;??????????????????????????????????????????????????????????????? ????????}?????????????????????????????????????????????????????????????????????????????????????????? ????????VM_BUG_ON_PAGE(batch->nr?>?batch->max,?page);?????????????????????????????????????????????? ??????????????????????????????????????????????????????????????????????????????????????????????????? ????????return?false;???//獲得???下一個批次??批次積聚結(jié)構(gòu)成功,返回??false;??????????????????????????????????????????????????????????????????????? }?????????????????????????????????????????????????????????????????????????????????????????????????? 

我們再來看下tlb_next_batch的實現(xiàn):

static?bool?tlb_next_batch(struct?mmu_gather?*tlb)??????????????????????????? { ????????struct?mmu_gather_batch?*batch; ???????? ????????batch?=?tlb->active;????????????????????????????????????????????????? ????????if?(batch->next)?{??//下一個批次積聚結(jié)構(gòu)存在 ????????????????tlb->active?=?batch->next;?//當(dāng)前的批次積聚結(jié)構(gòu)指向這個批次結(jié)構(gòu) ????????????????return?true; ????????}  ????????if?(tlb->batch_count?==?MAX_GATHER_BATCH_COUNT)??//如果批次數(shù)量達到最大值?則返回false ????????????????return?false;  ????????//批次還沒有到達最大值,則分配并初始化批次的積聚結(jié)構(gòu) ????????batch?=?(void?*)__get_free_pages(GFP_NOWAIT?|?__GFP_NOWARN,?0);??????//申請一個物理頁面由于存放  mmu_gather_batch和page數(shù)組 ????????if?(!batch) ????????????????return?false;  ????????tlb->batch_count++;???//批次計數(shù)加1 ????????batch->next?=?NULL; ????????batch->nr???=?0; ????????batch->max??=?MAX_GATHER_BATCH;???//批次積聚結(jié)構(gòu)的?page數(shù)組最大個數(shù)賦值為MAX_GATHER_BATCH ???????? ????????//插入到mmu?積聚結(jié)構(gòu)的批次鏈表中 ????????tlb->active->next?=?batch;??????????????????????????????????????????? ????????tlb->active?=?batch; ???????? ????????return?true;????????????????????????????????????????????????????????? }??????? 

這里有幾個地方需要注意:MAX_GATHER_BATCH_COUNT 表示的是mmu積聚操作最多可以有多少個批次積聚結(jié)構(gòu),他的值為10000UL/MAX_GATHER_BATCH (考慮到非搶占式內(nèi)核的soft lockups的影響)。MAX_GATHER_BATCH 表示一個批次的積聚結(jié)構(gòu)的 page數(shù)組的最多元素個數(shù),他的值為((PAGE_SIZE – sizeof(struct mmu_gather_batch)) / sizeof(void *)),也就是物理頁面大小去除掉struct mmu_gather_batch結(jié)構(gòu)大小。

下面給出相關(guān)圖解:

解除頁表過程:

深入理解Linux內(nèi)核之mmu-gather操作

添加的到積聚結(jié)構(gòu)page數(shù)組頁面小于等于8個的情況:

深入理解Linux內(nèi)核之mmu-gather操作

添加的到積聚結(jié)構(gòu)page數(shù)組頁面大于8個的情況:

1個批次積聚結(jié)構(gòu)->

深入理解Linux內(nèi)核之mmu-gather操作

2個批次積聚結(jié)構(gòu)->

深入理解Linux內(nèi)核之mmu-gather操作

更多批次積聚結(jié)構(gòu)加入->

深入理解Linux內(nèi)核之mmu-gather操作

2.5 free_pgtables

unmap_vmas函數(shù)主要是積聚了一些相關(guān)的虛擬頁面對應(yīng)的物理頁面,但是我們還需要釋放各級頁表對應(yīng)的物理頁等。下面看下free_pgtables的實現(xiàn):

首先看下它的主要脈絡(luò):

//?mm/memory.c void?free_pgtables(struct?mmu_gather?*tlb,?struct?vm_area_struct?*vma,????????????????????????????? ????????????????unsigned?long?floor,?unsigned?long?ceiling)???????????????????????????????????????? {?????????????????????????????????????????????????????????????????????????????????????????????????? ????????while?(vma)?{??????//從起始的vma開始遍歷每個vma????????????????????????????????? ????????????????struct?vm_area_struct?*next?=?vma->vm_next;????//獲得下一個vma???????????????? ????????????????unsigned?long?addr?=?vma->vm_start;?????//獲得vma的起始地址???????????????? ??????????????????????????????????????????????????????????????????????????????????????????????????? ????????????????/*????????????????????????????????????????????????????????????????????????????????? ????????????????|*?Hide?vma?from?rmap?and?truncate_pagecache?before?freeing???????????????????????? ????????????????|*?pgtables???????????????????????????????????????????????????????????????????????? ????????????????|*/???????????????????????????????????????????????????????????????????????????????? ????????????????unlink_anon_vmas(vma);???//解除匿名vma的反向映射關(guān)系??????????????????????? ????????????????unlink_file_vma(vma);??????//解除文件vma反向映射關(guān)系???????????????????????????????????????????????????????????????????????? ??????????????????????????????????????????????????????????????????????????????????????????????????? ????????????????if?(is_vm_hugetlb_page(vma))?{?????????????????????????????????????????????????????? ????????????????????????hugetlb_free_pgd_range(tlb,?addr,?vma->vm_end,????????????????????????????? ????????????????????????????????floor,?next???next->vm_start?:?ceiling);??????????????????????????? ????????????????}?else?{??????????????????????????????????????????????????????????????????????????? ????????????????????????/*????????????????????????????????????????????????????????????????????????? ????????????????????????|*?Optimization:?gather?nearby?vmas?into?one?call?down????????????????????? ????????????????????????|*/???????????????????????????????????????????????????????????????????????? ????????????????????????while?(next?&&?next->vm_start?vm_end?+?PMD_SIZE???????????????????? ????????????????????????|??????&&?!is_vm_hugetlb_page(next))?{????????????????????????????????????? ????????????????????????????????vma?=?next;???????????????????????????????????????????????????????? ????????????????????????????????next?=?vma->vm_next;??????????????????????????????????????????????? ????????????????????????????????unlink_anon_vmas(vma);????????????????????????????????????????????? ????????????????????????????????unlink_file_vma(vma);?????????????????????????????????????????????? ????????????????????????}?????????????????????????????????????????????????????????????????????????? ????????????????????????free_pgd_range(tlb,?addr,?vma->vm_end,????????????????????????????????????? ????????????????????????????????floor,?next???next->vm_start?:?ceiling);????//遍歷各級頁表???????????????????????? ????????????????}?????????????????????????????????????????????????????????????????????????????????? ????????????????vma?=?next;???????????????????????????????????????????????????????????????????????? ????????}?????????????????????????????????????????????????????????????????????????????????????????? }?????????????????????????????????????????????????????????????????????????????????????????????????? ??????????????????????????????????????????????????????????????????????????????????????????????????? 

我們主要看free_pgd_range的實現(xiàn):

free_pgd_range ->free_p4d_range? ????->free_pud_range? ????????->free_pmd_range?? ????????????->free_pte_range?? ????????????????->?.... ????????????->?pud_clear(pud);??////清除pud頁目錄中的對應(yīng)的pud表項 ????????????????pmd_free_tlb(tlb,?pmd,?start);?//pmd頁目錄的物理頁放入?頁表的積聚結(jié)構(gòu)中 ????????????????mm_dec_nr_pmds(tlb->mm)?//進程使用的頁表的物理頁統(tǒng)計減1 ????????->p4d_clear(p4d);??//清除p4d頁目錄中的對應(yīng)的p4d表項 ???????????pud_free_tlb(tlb,?pud,?start)??//pud頁目錄的物理頁放入?頁表的積聚結(jié)構(gòu)中 ????->?pgd_clear(pgd);??//清除pgd頁目錄中的對應(yīng)的pgd表項 ????????p4d_free_tlb(tlb,?p4d,?start);?//p4d頁目錄的物理頁放入?頁表的積聚結(jié)構(gòu)中(存在p4d頁目錄的話) 

我們以最后一級頁表(pmd表項指向)為例說明:

static?void?free_pte_range(struct?mmu_gather?*tlb,?pmd_t?*pmd, ????????????????????????|??unsigned?long?addr) { ????????pgtable_t?token?=?pmd_pgtable(*pmd);??//從相關(guān)的pmd表項指針中獲得頁表 ????????pmd_clear(pmd);???//清除pmd頁目錄中的對應(yīng)的pmd表項,即是頁表指針 ????????pte_free_tlb(tlb,?token,?addr);?//存放頁表的物理頁放入?頁表的積聚結(jié)構(gòu)中 ????????mm_dec_nr_ptes(tlb->mm);?//進程使用的頁表的物理頁統(tǒng)計減1 } 

看下pte_free_tlb函數(shù):

//?include/asm-generic/tlb.h #ifndef?pte_free_tlb #define?pte_free_tlb(tlb,?ptep,?address)???????????????????????? ????????do?{???????????????????????????????????????????????????? ????????????????tlb_flush_pmd_range(tlb,?address,?PAGE_SIZE);?????//更新tlb->start和tlb->end,?tlb->cleared_pmds?=?1 ????????????????tlb->freed_tables?=?1;??????????????????????????? ????????????????__pte_free_tlb(tlb,?ptep,?address);??????????????//存放頁表的物理頁放入頁表的積聚結(jié)構(gòu)中 ????????}?while?(0) #endif 

再看看__pte_free_tlb:

//?arch/arm64/include/asm/tlb.h __pte_free_tlb ->pgtable_pte_page_dtor(pte);?//執(zhí)行釋放頁表的時候的構(gòu)造函數(shù),如釋放ptlock內(nèi)存,zone的頁表頁面統(tǒng)計減1等 ????tlb_remove_table(tlb,?pte);??//mm/mmu_gather.c ????->??struct?mmu_table_batch?**batch?=?&tlb->batch;?//獲得?頁表的積聚結(jié)構(gòu)  ??????if?(*batch?==?NULL)?{??//如何為空,則分配一個物理頁,存放積聚結(jié)構(gòu)和積聚數(shù)組 ??????????????*batch?=?(struct?mmu_table_batch?*)__get_free_page(GFP_NOWAIT?|?__GFP_NOWARN); ??????????????if?(*batch?==?NULL)?{ ??????????????????????tlb_table_invalidate(tlb); ??????????????????????tlb_remove_table_one(table); ??????????????????????return; ??????????????} ??????????????(*batch)->nr?=?0; ??????}  ??????(*batch)->tables[(*batch)->nr++]?=?table;???//相關(guān)的頁目錄對應(yīng)的物理頁放入?積聚數(shù)組中 ??????if?((*batch)->nr?==?MAX_TABLE_BATCH)?//加入的物理頁達到最大值 ??????????????tlb_table_flush(tlb);??//做一次刷tlb和釋放當(dāng)前已經(jīng)積聚的頁目錄的物理頁 

需要說明的是:對于存放各級頁目錄的物理頁的釋放,每當(dāng)一個頁表積聚結(jié)構(gòu)填滿了就會釋放,不會構(gòu)建批次鏈表。

2.6 tlb_finish_mmu

通過上面的unmap_vmas和free_pgtables之后,我們積聚了大量的物理頁以及存放各級頁目錄的物理頁,現(xiàn)在需要將這些頁面進行釋放。

下面我們來看下tlb_finish_mmu做的mmu-gather的收尾動作:

void?tlb_finish_mmu(struct?mmu_gather?*tlb, ????????????????unsigned?long?start,?unsigned?long?end) { ??????...  ????????tlb_flush_mmu(tlb);??//刷tlb和釋放所有積聚的物理頁  #ifndef?CONFIG_MMU_GATHER_NO_GATHER ????????tlb_batch_list_free(tlb);?//釋放各批次結(jié)構(gòu)對應(yīng)的物理頁 #endif ?????... } 

首先看下tlb_flush_mmu:

mm/mmu_gather.c  ?void?tlb_flush_mmu(struct?mmu_gather?*tlb) ?{ ?????????tlb_flush_mmu_tlbonly(tlb);??//刷tlb ?????????tlb_flush_mmu_free(tlb);?//釋放各個批次積聚結(jié)構(gòu)的物理頁 ?} 

tlb_flush_mmu_tlbonly的實現(xiàn):

?static?inline?void?tlb_flush_mmu_tlbonly(struct?mmu_gather?*tlb) ?{ ?????????/* ?????????|*?Anything?calling?__tlb_adjust_range()?also?sets?at?least?one?of ?????????|*?these?bits. ?????????|*/ ?????????if?(!(tlb->freed_tables?||?tlb->cleared_ptes?||?tlb->cleared_pmds?|| ?????????|?????tlb->cleared_puds?||?tlb->cleared_p4ds))??//有一個為0?即返回 ?????????????????return; ? ?????????tlb_flush(tlb);??//刷tlb,和處理器架構(gòu)相關(guān) ???????... ?????????__tlb_reset_range(tlb);?//將tlb->start?和tlb->end以及tlb->freed_tables,tlb->cleared_xxx復(fù)位 ?} 

我們來看下tlb_flush:

arch/arm64/include/asm/tlb.h static?inline?void?tlb_flush(struct?mmu_gather?*tlb) { ????????struct?vm_area_struct?vma?=?TLB_FLUSH_VMA(tlb->mm,?0); ????????bool?last_level?=?!tlb->freed_tables; ????????unsigned?long?stride?=?tlb_get_unmap_size(tlb); ????????int?tlb_level?=?tlb_get_level(tlb);?//得到刷tlb的級別,如只刷pte級別  ????????/* ????????|*?If?we're?tearing?down?the?address?space?then?we?only?care?about ????????|*?invalidating?the?walk-cache,?since?the?ASID?allocator?won't ????????|*?reallocate?our?ASID?without?invalidating?the?entire?TLB. ????????|*/ ????????if?(tlb->fullmm)?{??//刷整個mm的tlb ????????????????if?(!last_level) ????????????????????????flush_tlb_mm(tlb->mm); ????????????????return; ????????} ???? ????????//刷一個虛擬內(nèi)存范圍的tlb ????????__flush_tlb_range(&vma,?tlb->start,?tlb->end,?stride, ????????????????????????|?last_level,?tlb_level); } 

最后我們看tlb_flush_mmu_free:

static?void?tlb_flush_mmu_free(struct?mmu_gather?*tlb) { ????????tlb_table_flush(tlb);??//釋放之前積聚的存放各級頁目錄的物理頁 #ifndef?CONFIG_MMU_GATHER_NO_GATHER ????????tlb_batch_pages_flush(tlb);?//釋放各個批次積聚結(jié)構(gòu)積聚的物理頁 #endif } 

tlb_table_flush的實現(xiàn):

?static?void?tlb_table_flush(struct?mmu_gather?*tlb) ?{ ?????????struct?mmu_table_batch?**batch?=?&tlb->batch;?//獲得當(dāng)前的?頁表批次?積聚結(jié)構(gòu) ????????? ?????????if?(*batch)?{ ?????????????????tlb_table_invalidate(tlb);?//刷tlb ?????????????????tlb_remove_table_free(*batch);?//釋放頁目錄物理頁 ?????????????????*batch?=?NULL; ?????????} ?} static?void?tlb_remove_table_free(struct?mmu_table_batch?*batch) { ????????call_rcu(&batch->rcu,?tlb_remove_table_rcu);?//rsu延遲調(diào)用 ????????->?__tlb_remove_table_free(container_of(head,?struct?mmu_table_batch,?rcu)); ????????????->?static?void?__tlb_remove_table_free(struct?mmu_table_batch?*batch) ?????????????{ ?????????????????????int?i;  ?????????????????????for?(i?=?0;?i?nr;?i++)?//釋放頁表批次?積聚結(jié)構(gòu)中的page數(shù)組中每一個物理頁 ?????????????????????????????__tlb_remove_table(batch->tables[i]);  ?????????????????????free_page((unsigned?long)batch);?//釋放這個?表批次?積聚結(jié)構(gòu)對應(yīng)的物理頁 ?????????????}???????  }??????? ? 

tlb_batch_pages_flush的實現(xiàn):

static?void?tlb_batch_pages_flush(struct?mmu_gather?*tlb) { ????????struct?mmu_gather_batch?*batch;  ????????for?(batch?=?&tlb->local;?batch?&&?batch->nr;?batch?=?batch->next)?{??//遍歷積聚批次鏈表的每一個?批  次積聚結(jié)構(gòu) ????????????????free_pages_and_swap_cache(batch->pages,?batch->nr);?//釋放積聚結(jié)構(gòu)的page數(shù)組的每一個物理頁 ????????????????batch->nr?=?0; ????????}??????? ????????tlb->active?=?&tlb->local; } 

最終是:調(diào)用free_pages_and_swap_cache將物理頁的引用計數(shù)減1 ,引用計數(shù)為0時就將這個物理頁釋放,還給伙伴系統(tǒng)。

雖然上面已經(jīng)釋放了相關(guān)的各級頁表的物理頁和映射到進程地址空間的物理頁,但是存放積聚結(jié)構(gòu)和page數(shù)組的物理頁還沒有釋放,所以調(diào)用tlb_batch_list_free來做這個事情:

tlb_batch_list_free ->?static?void?tlb_batch_list_free(struct?mmu_gather?*tlb) ?{ ?????????struct?mmu_gather_batch?*batch,?*next; ? ?????????for?(batch?=?tlb->local.next;?batch;?batch?=?next)?{??//釋放積聚結(jié)構(gòu)的物理頁從tlb->local.next開始  的,遍歷所有批次的積聚結(jié)構(gòu) ?????????????????next?=?batch->next; ?????????????????free_pages((unsigned?long)batch,?0);??//釋放這個批次積聚結(jié)構(gòu)的物理頁 ?????????}??????? ?????????tlb->local.next?=?NULL; ?} 

于是相關(guān)的所有物理頁面都被釋放了(包括相關(guān)地址范圍內(nèi)進程各級頁目錄對應(yīng)的物理頁,映射到進程地址空間的物理頁,和各個積聚結(jié)構(gòu)所在的物理頁)。

最后給出整體的圖解:

深入理解Linux內(nèi)核之mmu-gather操作

tlb_flush_mmu函數(shù)的tlb_table_flush會將B鏈表中的相關(guān)物理頁面釋放(包括之前保存的各級頁表的頁面和mmu_table_batch結(jié)構(gòu)所在頁面),tlb_batch_pages_flush會將A鏈表的所有除了積聚結(jié)構(gòu)以外的所有物理頁面釋放,而tlb_batch_list_free會將A鏈表的所有批次積聚結(jié)構(gòu)(mmu_gather_batch)的物理頁面釋放。

3.應(yīng)用場景

使用mmu-gather的應(yīng)用場景主要是進程退出,執(zhí)行execv和調(diào)用munmap等。

下面我們主要來看下他們的調(diào)用鏈:

3.1 進程退出時

進程退出時會釋放它的所有的相關(guān)聯(lián)的系統(tǒng)資源,其中就包括內(nèi)存資源:

kernel/exit.c do_exit ->exit_mm ????->mmput??//kernel/fork.c ????????->if?(atomic_dec_and_test(&mm->mm_users))??//如果mm->mm_users減1為0時,也就是當(dāng)前進程是最后一個mm的使用者 ????????__mmput(mm);?//釋放mm ????????->exit_mmap??//mm/mmap.c ????????????->tlb_gather_mmu(&tlb,?mm,?0,?-1);?//初始化mmu_gather結(jié)構(gòu),start=0,end=-1?表示釋放整個mm ????????????->unmap_vmas(&tlb,?vma,?0,?-1);?//解除頁表映射,相關(guān)的物理頁放入積聚結(jié)構(gòu)中 ????????????>free_pgtables(&tlb,?vma,?FIRST_USER_ADDRESS,?USER_PGTABLES_CEILING);?//釋放各級頁表,頁表相關(guān)物理頁放入頁表積聚結(jié)構(gòu),滿則釋放 ????????????>tlb_finish_mmu(&tlb,?0,?-1);?//刷mm的tlb,釋放所有積聚物理頁,釋放所有積聚結(jié)構(gòu)相關(guān)物理頁 

3.2 執(zhí)行execv時

執(zhí)行execv時進程會將所有的mm釋放掉:

fs/exec.c  ... do_execveat_common ->bprm_execve ????->exec_binprm ????????->search_binary_handler ????????????... ????????????->load_elf_binary?//?fs/binfmt_elf.c ????????????????->begin_new_exec ????????????????????->exec_mmap ????????????????????????->mmput(old_mm) ????????????????????????????->if?(atomic_dec_and_test(&mm->mm_users))??//如果mm->mm_users減1為0時,也就是當(dāng)前進程是最后一個mm的使用者 ???????????????????????????????__mmput(mm);?//釋放mm ????????????????????????????????->exit_mmap??//mm/mmap.c ?????????????????????????????????????->tlb_gather_mmu(&tlb,?mm,?0,?-1);?//初始化mmu_gather結(jié)構(gòu),start=0,end=-1標(biāo)識釋放整個mm ????????????????????????????????????->unmap_vmas(&tlb,?vma,?0,?-1);?//解除頁表映射,相關(guān)的物理頁放入積聚結(jié)構(gòu)中 ????????????????????????????????????->free_pgtables(&tlb,?vma,?FIRST_USER_ADDRESS,?USER_PGTABLES_CEILING);?//釋放各級頁表,頁表相關(guān)物理頁放入頁表積聚結(jié)構(gòu),滿則釋放 ?????????????????????????????????????->tlb_finish_mmu(&tlb,?0,?-1);?//刷mm的tlb,釋放所有積聚物理頁,釋放所有積聚結(jié)構(gòu)相關(guān)物理頁 

3.3 調(diào)用munmap時

執(zhí)行munmap時,會將一個地址范圍的頁表解除并釋放相關(guān)的物理頁面:

mm/mmap.c  ... __do_munmap ->unmap_region(mm,?vma,?prev,?start,?end);?? ????->?tlb_gather_mmu(&tlb,?mm,?start,?end);?//初始化mmu_gather結(jié)構(gòu) ?????unmap_vmas(&tlb,?vma,?start,?end);?//解除頁表映射,相關(guān)的物理頁放入積聚結(jié)構(gòu)中 ?????free_pgtables(&tlb,?vma,?prev???prev->vm_end?:?FIRST_USER_ADDRESS, ?????????????????????????????|next???next->vm_start?:?USER_PGTABLES_CEILING);?//釋放各級頁表,頁表相關(guān)物理頁放入頁表積聚結(jié)構(gòu),滿則釋放 ?????tlb_finish_mmu(&tlb,?start,?end);?//刷mm的tlb,釋放所有積聚物理頁,釋放所有積聚結(jié)構(gòu)相關(guān)物理頁 

4.總結(jié)

Linux內(nèi)核mmu-gather用于積聚解除映射的相關(guān)物理頁面,并保證了刷tlb和釋放物理頁面的順序。首先解除掉相關(guān)虛擬頁面對應(yīng)物理頁面(如果有的話)的頁表映射關(guān)系,然后將相關(guān)的物理頁面保存在積聚結(jié)構(gòu)的數(shù)組中,接著將相關(guān)的各級頁目錄表項清除,并放入頁表相關(guān)的積聚結(jié)構(gòu)的數(shù)組中,最后刷對應(yīng)內(nèi)存范圍的tlb,釋放掉所有放在積聚結(jié)構(gòu)數(shù)組中的物理頁面。

相關(guān)閱讀

主站蜘蛛池模板: 久久久久国产午夜 | 大片国产片日本观看免费视频 | 国产午夜免费不卡精品理论片 | 国产操操 | 欧美人成在线视频 | 亚洲一级毛片免费观看 | 国产一区二 | 亚洲免费视频一区二区三区 | 欧美一区二区三区免费不卡 | 黄色免费看片网站 | 中国国产一级毛片 | 高清国产精品久久 | 日本一级特大毛片 | 一区二区三区在线 | 九九久久国产精品 | 在线观看国产一级强片 | 一级片在线观看 | 亚洲精品久久99久久一区 | 国产精品久久一区 | 日本b站一卡二卡乱码入口 日本s色大片在线观看 | 91日本在线观看亚洲精品 | 美女视频永久黄网站在线观看 | 国产视频一区二区三区四区 | 国产黄色三级 | 男女男精品视频免费观看 | 欧美一二区视频 | 久久极品视频 | 88av视频在线 | 日韩久草 | 成人国产视频在线观看 | 99精品国产成人一区二区在线 | 亚洲一区二区三区在线播放 | 中国老太性色xxxxxhd | 国产成人精品久久亚洲高清不卡 | 欧美成人免费香蕉 | 国产精品99久久久久久www | 欧美色老头oldvideo | 欧美成人免费午夜全 | 222aaa天堂| 青青久草 | 日本视频在线观看不卡高清免费 |