自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Linux內(nèi)核反向映射RMAP:加速數(shù)據(jù)訪問的關(guān)鍵技術(shù)

系統(tǒng) Linux
在計(jì)算機(jī)系統(tǒng)中,內(nèi)存管理一直是操作系統(tǒng)的核心功能之一,對于 Linux 系統(tǒng)而言,其內(nèi)存管理機(jī)制隨著時(shí)間的推移不斷演進(jìn),從早期的簡單形式逐漸發(fā)展為如今復(fù)雜而高效的體系。

而今天要深入剖析的Linux內(nèi)核反向映射 RMAP(Reverse Mapping),正是一把能大幅加速數(shù)據(jù)訪問的利刃,它在內(nèi)存管理的舞臺上扮演著舉足輕重的角色。從基礎(chǔ)概念到復(fù)雜的實(shí)現(xiàn)機(jī)制,RMAP 背后藏著諸多奧秘,接下來,就讓我們一同踏上探索之旅,揭開它的神秘面紗 。

一、Linux 內(nèi)存管理的 “前世今生”

在計(jì)算機(jī)系統(tǒng)中,內(nèi)存管理一直是操作系統(tǒng)的核心功能之一,對于 Linux 系統(tǒng)而言,其內(nèi)存管理機(jī)制隨著時(shí)間的推移不斷演進(jìn),從早期的簡單形式逐漸發(fā)展為如今復(fù)雜而高效的體系。

早期的計(jì)算機(jī)系統(tǒng)中,內(nèi)存管理非常直接和簡單。程序直接運(yùn)行在物理內(nèi)存上,采用連續(xù)分配的方式,將物理內(nèi)存劃分為不同的區(qū)域,每個(gè)區(qū)域分配給一個(gè)程序使用。這種方式雖然簡單易懂,但存在諸多嚴(yán)重問題,比如進(jìn)程地址空間無法隔離,一個(gè)進(jìn)程可以隨意訪問其他進(jìn)程的內(nèi)存空間,這嚴(yán)重威脅系統(tǒng)安全;內(nèi)存使用效率低下,由于需要連續(xù)的內(nèi)存空間,容易產(chǎn)生內(nèi)存碎片,導(dǎo)致內(nèi)存浪費(fèi);而且程序運(yùn)行地址不確定,每次運(yùn)行都要在內(nèi)存中尋找足夠大的空閑區(qū)域,增加了程序重定位的復(fù)雜性。

為了解決這些問題,虛擬內(nèi)存的概念應(yīng)運(yùn)而生。虛擬內(nèi)存作為程序和物理內(nèi)存之間的中間層,為每個(gè)進(jìn)程提供獨(dú)立的地址空間,實(shí)現(xiàn)進(jìn)程地址空間的隔離,增強(qiáng)系統(tǒng)安全性。在 Linux 系統(tǒng)中,虛擬內(nèi)存通過分頁和分段技術(shù)實(shí)現(xiàn)虛擬地址到物理地址的映射。分段技術(shù)將程序地址空間劃分為不同邏輯段,如代碼段、數(shù)據(jù)段、棧段等,每個(gè)段在物理內(nèi)存中可以不連續(xù)存儲,解決程序運(yùn)行地址不確定問題,但仍存在外部碎片問題。分頁技術(shù)則把虛擬內(nèi)存和物理內(nèi)存劃分為固定大小的頁,以頁為單位進(jìn)行映射和管理,大大提高內(nèi)存利用率,減少內(nèi)存碎片。

隨著 Linux 系統(tǒng)應(yīng)用場景不斷擴(kuò)展和硬件技術(shù)發(fā)展,內(nèi)存管理面臨新挑戰(zhàn)。特別是在多進(jìn)程、多線程環(huán)境下,如何快速準(zhǔn)確地確定物理頁面與虛擬頁面之間的映射關(guān)系,成為提高內(nèi)存管理效率的關(guān)鍵。在早期 Linux 內(nèi)核版本中(如 2.4 內(nèi)核),當(dāng)需要確定某物理頁面是否被某個(gè)進(jìn)程映射時(shí),必須遍歷每個(gè)進(jìn)程的頁表,這一過程工作量巨大,效率極低。比如在一個(gè)擁有大量進(jìn)程的服務(wù)器系統(tǒng)中,若要查找某個(gè)物理頁面的映射關(guān)系,遍歷所有進(jìn)程頁表可能需要耗費(fèi)大量 CPU 時(shí)間,嚴(yán)重影響系統(tǒng)性能。

為應(yīng)對這一挑戰(zhàn),在 Linux 2.5 內(nèi)核開發(fā)期間,反向映射(Reverse Mapping,RMAP)的概念被提出并逐步完善。RMAP 的出現(xiàn),徹底改變 Linux 內(nèi)存管理中查找頁面映射關(guān)系的方式,極大提高內(nèi)存管理效率,為 Linux 系統(tǒng)在各種復(fù)雜環(huán)境下穩(wěn)定高效運(yùn)行奠定堅(jiān)實(shí)基礎(chǔ)。

二、RMAP是什么?

RMAP,即反向映射(Reverse Mapping),是 Linux 內(nèi)核中用于解決從物理頁面快速查找其對應(yīng)的虛擬地址映射關(guān)系的關(guān)鍵機(jī)制 ,與傳統(tǒng)的從虛擬地址到物理地址的正向映射方向相反,RMAP 建立了從物理頁面到虛擬地址空間的反向映射關(guān)系。在實(shí)際運(yùn)行中,當(dāng)系統(tǒng)需要對某個(gè)物理頁面進(jìn)行操作時(shí),如回收、遷移或共享,RMAP 能幫助內(nèi)核迅速定位到所有映射到該物理頁面的虛擬地址,極大提高操作效率。

在頁面回收場景中,當(dāng)系統(tǒng)內(nèi)存不足時(shí),需要將一些長時(shí)間未使用的頁面從物理內(nèi)存中回收,釋放出空間供其他更急需內(nèi)存的進(jìn)程使用。在沒有 RMAP 機(jī)制之前,要回收一個(gè)物理頁面,內(nèi)核必須遍歷系統(tǒng)中每個(gè)進(jìn)程的頁表,檢查該物理頁面是否被某個(gè)進(jìn)程映射,這一過程效率極低,因?yàn)楝F(xiàn)代計(jì)算機(jī)系統(tǒng)中往往運(yùn)行著大量進(jìn)程,每個(gè)進(jìn)程又有龐大的頁表,遍歷所有進(jìn)程頁表的時(shí)間開銷巨大。而有了 RMAP 后,內(nèi)核可以直接通過物理頁面的相關(guān)數(shù)據(jù)結(jié)構(gòu),快速找到所有映射該頁面的虛擬地址,然后斷開這些映射關(guān)系,將頁面回收,大大提高內(nèi)存回收效率。

在頁面遷移場景中,為了實(shí)現(xiàn)內(nèi)存的高效利用和負(fù)載均衡,有時(shí)需要將一個(gè)物理頁面從一個(gè)內(nèi)存區(qū)域遷移到另一個(gè)內(nèi)存區(qū)域,例如在 NUMA(Non - Uniform Memory Access)架構(gòu)的系統(tǒng)中,為了讓進(jìn)程更高效地訪問內(nèi)存,可能需要將進(jìn)程使用的頁面遷移到距離其 CPU 更近的內(nèi)存節(jié)點(diǎn)上。在遷移之前,同樣需要找到所有映射到該頁面的虛擬地址,以便在遷移完成后更新映射關(guān)系。如果沒有 RMAP,查找這些映射關(guān)系的過程會非常耗時(shí),影響系統(tǒng)性能;而RMAP 的存在使得這一查找過程變得快速而準(zhǔn)確,確保頁面遷移能夠順利進(jìn)行。

RMAP 機(jī)制對于 Linux 系統(tǒng)的內(nèi)存管理效率提升至關(guān)重要,它是解決復(fù)雜內(nèi)存管理問題的核心方案,為系統(tǒng)在各種內(nèi)存操作場景下的高效運(yùn)行提供了有力支持。

2.1正向映射

當(dāng)進(jìn)程分配內(nèi)存并發(fā)生寫操作時(shí),會分配虛擬地址并產(chǎn)生缺頁,進(jìn)而分配物理內(nèi)存并建立虛擬地址到物理地址的映射關(guān)系, 這個(gè)叫正向映射。

圖片圖片

2.2反向映射

反過來, 通過物理頁面找到映射它的所有虛擬頁面叫反向映射(reverse-mapping, RMAP)。

圖片圖片

2.3RMAP的背景

用戶進(jìn)程在使用虛擬內(nèi)存的過程中,從虛擬內(nèi)存頁面映射到物理內(nèi)存頁面時(shí),PTE保留這個(gè)記錄,page數(shù)據(jù)結(jié)構(gòu)中的_mapcount記錄有多少個(gè)用戶PTE映射到物理頁面。用戶PTE是指用戶進(jìn)程地址空間和物理頁面建立映射的PTE,不包括內(nèi)核地址空間映射物理頁面時(shí)產(chǎn)生的PTE。有的頁面需要遷移,有的頁面長時(shí)間不使用,需要交換到磁盤。在交換之前,必須找出哪些進(jìn)程使用這個(gè)頁面,然后解除這些映射的用戶PTE。一個(gè)物理頁面可以同時(shí)被多個(gè)進(jìn)程的虛擬內(nèi)存映射,但是一個(gè)虛擬頁面同時(shí)只能映射到一個(gè)物理頁面。

在Linux 2.4內(nèi)核中,為了確定某一個(gè)頁面是否被某個(gè)進(jìn)程映射,必須遍歷每個(gè)進(jìn)程的頁表,因此工作量相當(dāng)大,效率很低。在Linux2.5內(nèi)核開發(fā)期間,提出了反向映射(Reverse Mapping,RMAP)的概念。

三、RMAP 關(guān)鍵數(shù)據(jù)結(jié)構(gòu)剖析

RMAP的主要目的是從物理頁面的page數(shù)據(jù)結(jié)構(gòu)中找到有哪些映射的用戶PTE,這樣頁面回收模塊就可以很快速和高效地把這個(gè)物理頁面映射的所有用戶PTE都解除并回收這個(gè)頁面。在深入了解 RMAP 的工作原理之前,我們先來剖析一下支撐 RMAP 機(jī)制運(yùn)行的關(guān)鍵數(shù)據(jù)結(jié)構(gòu),它們是 RMAP 實(shí)現(xiàn)高效反向映射的基石,理解這些數(shù)據(jù)結(jié)構(gòu)的構(gòu)成和相互關(guān)系,對于掌握 RMAP 的核心思想至關(guān)重要。

為了達(dá)到這個(gè)目的,內(nèi)核在頁面創(chuàng)建時(shí)需要建立RMAP的“鉤子”,即建立相關(guān)的數(shù)據(jù)結(jié)構(gòu),RMAP系統(tǒng)中有兩個(gè)重要的數(shù)據(jù)結(jié)構(gòu):一個(gè)是anon_vma,簡稱AV;另一個(gè)是anon_vma_chain,簡稱AVC。

3.1struct anon_vma(AV)

struct anon_vma 是 RMAP 機(jī)制中用于管理匿名內(nèi)存映射區(qū)域的關(guān)鍵數(shù)據(jù)結(jié)構(gòu),在連接物理頁面的 page 數(shù)據(jù)結(jié)構(gòu)和虛擬內(nèi)存區(qū)域的 vm_area_struct(VMA) 中扮演著核心角色 。當(dāng)進(jìn)程創(chuàng)建匿名內(nèi)存映射(比如通過 malloc 分配內(nèi)存,在缺頁異常時(shí)創(chuàng)建的匿名頁面)時(shí),anon_vma 就開始發(fā)揮作用。

從定義來看,struct anon_vma 包含多個(gè)重要字段:

struct anon_vma {
    struct anon_vma *root;         /* Root of this anon_vma tree */
    struct rw_semaphore rwsem;     /* W: modification, R: walking the list */
    atomic_t refcount;             /* 引用計(jì)數(shù),記錄當(dāng)前有多少個(gè)VMA引用了該anon_vma */
    unsigned degree;               /* Count of child anon_vmas and VMAs which points to this anon_vma */
    struct anon_vma *parent;       /* Parent of this anon_vma */
    struct rb_root_cached rb_root; /* Interval tree of private "related" vmas */
};

其中,refcount 字段用于引用計(jì)數(shù),它記錄著當(dāng)前有多少個(gè) VMA 引用了該 anon_vma。當(dāng)一個(gè)新的 VMA 與該 anon_vma 建立關(guān)聯(lián)時(shí),refcount 會增加;反之,當(dāng) VMA 與 anon_vma 解除關(guān)聯(lián)時(shí),refcount 減少。當(dāng) refcount 變?yōu)?0 時(shí),說明沒有 VMA 再引用這個(gè) anon_vma,此時(shí)就可以對 anon_vma 進(jìn)行回收。

rb_root 字段是一個(gè)紅黑樹的根節(jié)點(diǎn),通過這個(gè)紅黑樹,anon_vma 可以高效地管理和查找與它相關(guān)的 VMA。在實(shí)際場景中,當(dāng)系統(tǒng)需要查找所有引用某個(gè)匿名頁面的 VMA 時(shí),就可以通過 page 結(jié)構(gòu)中的 mapping 指針找到對應(yīng)的 anon_vma,然后遍歷 anon_vma 的紅黑樹,快速獲取所有相關(guān)的 VMA,大大提高查找效率。

3.2struct anon_vma_chain(AVC)

struct anon_vma_chain 是連接 vm_area_struct(VMA) 和 anon_vma 的橋梁,在 RMAP 機(jī)制中起著不可或缺的樞紐作用。它主要用于維護(hù) VMA 和 anon_vma 之間的關(guān)聯(lián)關(guān)系,使得內(nèi)核能夠方便地從 VMA 找到對應(yīng)的 anon_vma,反之亦然。

struct anon_vma_chain {
    struct vm_area_struct *vma;         // 指向?qū)?yīng)的VMA
    struct anon_vma *anon_vma;         // 指向?qū)?yīng)的anon_vma
    struct list_head same_vma;         // 用于鏈接與同一VMA相關(guān)的所有anon_vma_chain節(jié)點(diǎn)
    struct rb_node rb;                 // 用于將anon_vma_chain節(jié)點(diǎn)插入到anon_vma的紅黑樹中
    unsigned long rb_subtree_last;
#ifdef CONFIG_DEBUG_VM_RB
    unsigned long cached_vma_start, cached_vma_last;
#endif
};

在進(jìn)程創(chuàng)建子進(jìn)程時(shí),子進(jìn)程會復(fù)制父進(jìn)程的地址空間和頁表。此時(shí),anon_vma_chain 就會發(fā)揮作用,它會在子進(jìn)程的 VMA 和父進(jìn)程的 anon_vma 之間建立連接。具體來說,子進(jìn)程的每個(gè) VMA 都會創(chuàng)建一個(gè) anon_vma_chain 節(jié)點(diǎn),該節(jié)點(diǎn)的 vma 指針指向子進(jìn)程的 VMA,anon_vma 指針指向父進(jìn)程的 anon_vma,然后通過 same_vma 鏈表和 rb 紅黑樹節(jié)點(diǎn),將這個(gè) anon_vma_chain 節(jié)點(diǎn)插入到相應(yīng)的鏈表和紅黑樹中,從而實(shí)現(xiàn)子進(jìn)程 VMA 與父進(jìn)程 anon_vma 的關(guān)聯(lián) 。這樣,當(dāng)系統(tǒng)需要對某個(gè)匿名頁面進(jìn)行操作時(shí),就可以通過 anon_vma_chain 快速找到所有與該頁面相關(guān)的 VMA,無論是在父進(jìn)程還是子進(jìn)程中。

3.3struct vm_area_struct(VMA)

struct vm_area_struct 用于描述進(jìn)程地址空間中的一段虛擬內(nèi)存區(qū)域,是進(jìn)程虛擬內(nèi)存管理的重要數(shù)據(jù)結(jié)構(gòu),在 RMAP 機(jī)制中也有著關(guān)鍵作用,它記錄了虛擬內(nèi)存區(qū)域的起始地址、結(jié)束地址、訪問權(quán)限、所屬的內(nèi)存描述符等重要信息,與 RMAP 相關(guān)的字段主要有 anon_vma_chain 和 anon_vma。

struct vm_area_struct {
    unsigned long vm_start;         // 虛擬內(nèi)存區(qū)域的起始地址
    unsigned long vm_end;           // 虛擬內(nèi)存區(qū)域的結(jié)束地址
    struct mm_struct *vm_mm;       // 指向所屬的內(nèi)存描述符
    struct vm_area_struct *vm_next; // 指向下一個(gè)虛擬內(nèi)存區(qū)域
    pgprot_t vm_page_prot;         // 頁面保護(hù)標(biāo)志
    unsigned long vm_flags;        // 虛擬內(nèi)存區(qū)域的標(biāo)志
    struct list_head anon_vma_chain; // 用于鏈接anon_vma_chain節(jié)點(diǎn),通過這個(gè)鏈表可以找到與該VMA相關(guān)的所有anon_vma_chain
    struct anon_vma *anon_vma;     // 指向該VMA對應(yīng)的anon_vma
    // 其他字段...
};

在進(jìn)程運(yùn)行過程中,當(dāng)發(fā)生缺頁異常需要?jiǎng)?chuàng)建新的匿名頁面時(shí),內(nèi)核會為該頁面所在的 VMA 分配一個(gè) anon_vma,并通過 anon_vma_chain 將 VMA 和 anon_vma 連接起來。例如,當(dāng)一個(gè)進(jìn)程調(diào)用 malloc 分配內(nèi)存時(shí),會在進(jìn)程的地址空間中創(chuàng)建一個(gè)新的 VMA 來管理這塊內(nèi)存,同時(shí)為這個(gè) VMA 關(guān)聯(lián)一個(gè) anon_vma,并通過 anon_vma_chain 建立兩者之間的聯(lián)系。這樣,在后續(xù)的內(nèi)存操作中,如頁面回收、遷移等,就可以通過 VMA 的這些字段快速找到相關(guān)的 anon_vma 和其他關(guān)聯(lián)信息,從而高效地完成內(nèi)存管理任務(wù)。

四、RMAP的工作流程與原理

4.1匿名頁面的創(chuàng)建與 RMAP 初始化

在進(jìn)程運(yùn)行過程中,當(dāng)訪問的虛擬地址尚未映射到物理頁面時(shí),會觸發(fā)缺頁異常。此時(shí),內(nèi)核會調(diào)用 do_anonymous_page 函數(shù)來處理匿名頁面的創(chuàng)建。以 do_anonymous_page 函數(shù)為例,當(dāng)進(jìn)程調(diào)用 malloc 分配內(nèi)存時(shí),如果相應(yīng)的虛擬地址尚未映射物理頁面,就會觸發(fā)缺頁異常,進(jìn)而調(diào)用 do_anonymous_page 函數(shù)。在這個(gè)函數(shù)中,會先后調(diào)用 anon_vma_prepare 和 page_add_new_anon_rmap 函數(shù)來完成 RMAP 相關(guān)的初始化工作。

static vm_fault_t do_anonymous_page(struct vm_fault *vmf) {
    struct vm_area_struct *vma = vmf->vma;
    struct page *page;
    vm_fault_t ret = 0;
    pte_t entry;

    // 準(zhǔn)備anon_vma
    if (unlikely(anon_vma_prepare(vma)))
        goto oom;

    // 分配物理頁面
    page = alloc_zeroed_user_highpage_movable(vma, vmf->address);
    if (!page)
        goto oom;

    // 添加新的匿名反向映射
    page_add_new_anon_rmap(page, vma, vmf->address, false);

    // 其他處理...
    return ret;

oom:
    return VM_FAULT_OOM;
}

anon_vma_prepare 函數(shù)主要負(fù)責(zé)為 VMA 分配和初始化 anon_vma 結(jié)構(gòu),并建立 anon_vma_chain 連接。在這個(gè)函數(shù)中,首先嘗試查找是否存在可合并的 anon_vma,如果不存在則分配一個(gè)新的 anon_vma。然后,通過 anon_vma_chain_alloc 分配一個(gè) anon_vma_chain 結(jié)構(gòu),并將其與 VMA 和 anon_vma 進(jìn)行關(guān)聯(lián)。具體來說,它會將 anon_vma_chain 的 vma 指針指向 VMA,anon_vma 指針指向分配的 anon_vma,然后將 anon_vma_chain 添加到 VMA 的 anon_vma_chain 鏈表和 anon_vma 的紅黑樹中,這樣就建立了 VMA 和 anon_vma 之間的雙向關(guān)聯(lián)。

int __anon_vma_prepare(struct vm_area_struct *vma) {
    struct anon_vma *anon_vma, *allocated;
    struct anon_vma_chain *avc;

    // 分配anon_vma_chain
    avc = anon_vma_chain_alloc(GFP_KERNEL);
    anon_vma = find_mergeable_anon_vma(vma);
    allocated = NULL;

    if (!anon_vma) {
        // 分配新的anon_vma
        anon_vma = anon_vma_alloc();
        allocated = anon_vma;
    }

    anon_vma_lock_write(anon_vma);
    spin_lock(&mm->page_table_lock);

    if (likely(!vma->anon_vma)) {
        vma->anon_vma = anon_vma;
        // 鏈接anon_vma_chain
        anon_vma_chain_link(vma, avc, anon_vma);
    }

    // 解鎖相關(guān)鎖
    spin_unlock(&mm->page_table_lock);
    anon_vma_unlock_write(anon_vma);

    return 0;
}

page_add_new_anon_rmap 函數(shù)則將新分配的物理頁面與 VMA 建立反向映射關(guān)系。它會設(shè)置頁面的 mapping 字段指向 anon_vma,并計(jì)算頁面在 VMA 中的索引值存儲在 index 字段中。具體實(shí)現(xiàn)中,先設(shè)置頁面的 SwapBacked 標(biāo)志,表示該頁面可交換到磁盤。然后根據(jù)頁面是否為復(fù)合頁(如大頁),設(shè)置 mapcount 字段。接著,通過 __page_set_anon_rmap 函數(shù)將 anon_vma 加上 PAGE_MAPPING_ANON 后賦值給頁面的 mapping 字段,并計(jì)算頁面在 VMA 中的線性索引值賦值給 index 字段,從而完成物理頁面與 VMA 的反向映射關(guān)聯(lián) 。

void page_add_new_anon_rmap(struct page *page, struct vm_area_struct *vma, unsigned long address, bool compound) {
    int nr = compound? hpage_nr_pages(page) : 1;

    VM_BUG_ON_VMA(address < vma->vm_start || address >= vma->vm_end, vma);

    __SetPageSwapBacked(page);

    if (compound) {
        VM_BUG_ON_PAGE(!PageTransHuge(page), page);
        atomic_set(compound_mapcount_ptr(page), 0);
        __inc_node_page_state(page, NR_ANON_THPS);
    } else {
        VM_BUG_ON_PAGE(PageTransCompound(page), page);
        atomic_set(&page->mapcount, 0);
    }

    __mod_node_page_state(page_pgdat(page), NR_ANON_MAPPED, nr);
    __page_set_anon_rmap(page, vma, address, 1);
}

4.2子進(jìn)程創(chuàng)建時(shí)的 RMAP 處理

當(dāng)父進(jìn)程通過 fork 創(chuàng)建子進(jìn)程時(shí),子進(jìn)程需要復(fù)制父進(jìn)程的地址空間及頁表。在這個(gè)過程中,dup_mmap 函數(shù)負(fù)責(zé)復(fù)制父進(jìn)程的進(jìn)程地址空間,遍歷父進(jìn)程的 VMA 鏈表,為每個(gè) VMA 創(chuàng)建一個(gè)新的 VMA 數(shù)據(jù)結(jié)構(gòu),并將父進(jìn)程 VMA 中的相關(guān)信息復(fù)制到子進(jìn)程的 VMA 中。

static __latent_entropy int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) {
    struct vm_area_struct *mpnt, *tmp;
    int retval;

    for (mpnt = oldmm->map; mpnt; mpnt = mpnt->vm_next) {
        // 創(chuàng)建臨時(shí)VMA數(shù)據(jù)結(jié)構(gòu)tmp,把父進(jìn)程VMA復(fù)制到子進(jìn)程剛創(chuàng)建的tmp中
        tmp = vm_area_dup(mpnt);
        if (!tmp) {
            retval = -ENOMEM;
            goto free_vmas;
        }

        tmp->vm_mm = mm;

        // 為子進(jìn)程創(chuàng)建相應(yīng)anon_vma數(shù)據(jù)結(jié)構(gòu)并添加到紅黑樹
        if (anon_vma_fork(tmp, mpnt)) {
            __vma_link_rb(mm, tmp, rb_link, rb_parent);
        }

        // 復(fù)制父進(jìn)程的PTE到子進(jìn)程
        if (!(tmp->vm_flags & VM_WIPEONFORK)) {
            retval = copy_page_range(tmp, mpnt);
            if (retval)
                goto unlink_vma;
        }
    }

    return 0;

unlink_vma:
    __vma_unlink_rb(mm, tmp);
free_vmas:
    // 釋放相關(guān)資源
    return retval;
}

anon_vma_fork 函數(shù)在子進(jìn)程創(chuàng)建過程中起著關(guān)鍵作用,負(fù)責(zé)為子進(jìn)程的 VMA 創(chuàng)建相應(yīng)的 anon_vma 數(shù)據(jù)結(jié)構(gòu),并建立子進(jìn)程 VMA 與父進(jìn)程 anon_vma 之間的關(guān)聯(lián)。如果父進(jìn)程的 VMA 沒有關(guān)聯(lián)的 anon_vma,則直接返回。否則,嘗試克隆父進(jìn)程的 anon_vma 到子進(jìn)程。如果克隆失敗,則分配一個(gè)新的 anon_vma 給子進(jìn)程,并將其與子進(jìn)程的 VMA 通過 anon_vma_chain 連接起來,同時(shí)設(shè)置相關(guān)的父子關(guān)系和引用計(jì)數(shù) 。

int anon_vma_fork(struct vm_area_struct *vma, struct vm_area_struct *pvma) {
    struct anon_vma_chain *avc;
    struct anon_vma *anon_vma;
    int error;

    if (!pvma->anon_vma)
        return 0;

    error = anon_vma_clone(vma, pvma);
    if (vma->anon_vma)
        return 0;

    anon_vma = anon_vma_alloc();
    avc = anon_vma_chain_alloc(GFP_KERNEL);

    anon_vma->root = pvma->anon_vma->root;
    anon_vma->parent = pvma->anon_vma;
    get_anon_vma(anon_vma->root);

    vma->anon_vma = anon_vma;
    // 鏈接anon_vma_chain
    anon_vma_chain_link(vma, avc, anon_vma);

    return 0;
}

在實(shí)際場景中,假設(shè)父進(jìn)程有一個(gè) VMA 用于管理堆內(nèi)存,當(dāng)通過 fork 創(chuàng)建子進(jìn)程時(shí),子進(jìn)程會復(fù)制父進(jìn)程的這個(gè) VMA 結(jié)構(gòu),并通過 anon_vma_fork 函數(shù)建立與父進(jìn)程 anon_vma 的關(guān)聯(lián)。這樣,在后續(xù)內(nèi)存操作中,系統(tǒng)可以通過 RMAP 機(jī)制統(tǒng)一管理父子進(jìn)程中與該 VMA 相關(guān)的匿名頁面,實(shí)現(xiàn)內(nèi)存的高效利用和共享。

4.3頁面回收與遷移時(shí)的 RMAP 應(yīng)用

當(dāng)系統(tǒng)內(nèi)存不足時(shí),kswapd 內(nèi)核線程會啟動頁面回收機(jī)制,查找可以回收的頁面。對于匿名頁面,kswapd 會利用 RMAP 機(jī)制來找到所有映射該頁面的 PTE 并斷開映射。具體來說,kswapd 會調(diào)用 try_to_unmap 函數(shù),該函數(shù)是反向映射的核心函數(shù)之一,它會遍歷頁面的反向映射關(guān)系,找到所有映射該頁面的 VMA,并調(diào)用 try_to_unmap_one 函數(shù)來斷開每個(gè) VMA 中對該頁面的映射。

int try_to_unmap(struct page *page, enum ttu_flags flags) {
    int ret;
    struct rmap_walk_control rwc = {
      .rmap_one = try_to_unmap_one,
      .arg = (void *)flags,
      .done = page_not_mapped,
      .anon_lock = page_lock_anon_vma_read,
    };

    VM_BUG_ON_PAGE(!PageHuge(page) && PageTransHuge(page), page);

    if ((flags & TTU_MIGRATION) &&!PageKsm(page) && PageAnon(page))
        rwc.invalid_vma = invalid_migration_vma;

    ret = rmap_walk(page, &rwc);

    if (ret != SWAP_MLOCK &&!page_mapped(page))
        ret = SWAP_SUCCESS;

    return ret;
}

在頁面遷移場景中,當(dāng)需要將一個(gè)物理頁面從一個(gè)內(nèi)存區(qū)域遷移到另一個(gè)內(nèi)存區(qū)域時(shí),同樣需要利用 RMAP 機(jī)制。例如在 NUMA 架構(gòu)系統(tǒng)中,為了優(yōu)化內(nèi)存訪問性能,可能需要將某個(gè)進(jìn)程使用的頁面遷移到距離其 CPU 更近的內(nèi)存節(jié)點(diǎn)上。在遷移過程中,首先通過 RMAP 找到所有映射該頁面的 PTE,然后將這些 PTE 標(biāo)記為無效,并在目標(biāo)內(nèi)存區(qū)域分配新的物理頁面。遷移完成后,再更新 PTE 的映射關(guān)系,指向新的物理頁面 。整個(gè)過程中,RMAP 機(jī)制確保了在頁面遷移前后,所有相關(guān)進(jìn)程的頁表能夠正確更新,保證進(jìn)程對內(nèi)存的正常訪問。

五、反向映射RMAP應(yīng)用

內(nèi)核中通過struct page找到所有映射到這個(gè)頁面的VMA典型場景有:

  1. kswapd內(nèi)核線程回收頁面需要斷開所有映射了該匿名頁面的用戶PTE頁表項(xiàng)。
  2. 頁面遷移時(shí),需要斷開所有映射到匿名頁面的用戶PTE頁表項(xiàng)。

try_to_unmap()是反向映射的核心函數(shù),內(nèi)核中其他模塊會調(diào)用此函數(shù)來斷開一個(gè)頁面的所有映射:

/**
 * try_to_unmap - try to remove all page table mappings to a page
 * @page: the page to get unmapped
 * @flags: action and flags
 *
 * Tries to remove all the page table entries which are mapping this
 * page, used in the pageout path.  Caller must hold the page lock.
 * Return values are:
 *
 * SWAP_SUCCESS    - we succeeded in removing all mappings------------成功解除了所有映射的PTE。
 * SWAP_AGAIN    - we missed a mapping, try again later---------------可能錯(cuò)過了一個(gè)映射的PTE,需要重來一次。
 * SWAP_FAIL    - the page is unswappable-----------------------------失敗
 * SWAP_MLOCK    - page is mlocked.-----------------------------------頁面被鎖住了
 */
int try_to_unmap(struct page *page, enum ttu_flags flags)
{
    int ret;
    struct rmap_walk_control rwc = {
        .rmap_one = try_to_unmap_one,--------------------------------具體斷開某個(gè)VMA上映射的pte
        .arg = (void *)flags,
        .done = page_not_mapped,-------------------------------------判斷一個(gè)頁面是否斷開成功的條件
        .anon_lock = page_lock_anon_vma_read,------------------------鎖
    };

    VM_BUG_ON_PAGE(!PageHuge(page) && PageTransHuge(page), page);

    /*
     * During exec, a temporary VMA is setup and later moved.
     * The VMA is moved under the anon_vma lock but not the
     * page tables leading to a race where migration cannot
     * find the migration ptes. Rather than increasing the
     * locking requirements of exec(), migration skips
     * temporary VMAs until after exec() completes.
     */
    if ((flags & TTU_MIGRATION) && !PageKsm(page) && PageAnon(page))
        rwc.invalid_vma = invalid_migration_vma;

    ret = rmap_walk(page, &rwc);

    if (ret != SWAP_MLOCK && !page_mapped(page))
        ret = SWAP_SUCCESS;
    return ret;
}

內(nèi)核中有三種頁面需要unmap操作,即KSM頁面、匿名頁面、文件映射頁面:

int rmap_walk(struct page *page, struct rmap_walk_control *rwc)
{
    if (unlikely(PageKsm(page)))
        return rmap_walk_ksm(page, rwc);
    else if (PageAnon(page))
        return rmap_walk_anon(page, rwc);
    else
        return rmap_walk_file(page, rwc);
}

面以匿名頁面的unmap為例:

static int rmap_walk_anon(struct page *page, struct rmap_walk_control *rwc)
{
    struct anon_vma *anon_vma;
    pgoff_t pgoff;
    struct anon_vma_chain *avc;
    int ret = SWAP_AGAIN;

    anon_vma = rmap_walk_anon_lock(page, rwc);-----------------------------------獲取頁面page->mapping指向的anon_vma數(shù)據(jù)結(jié)構(gòu),并申請一個(gè)讀者鎖。
    if (!anon_vma)
        return ret;

    pgoff = page_to_pgoff(page);
    anon_vma_interval_tree_foreach(avc, &anon_vma->rb_root, pgoff, pgoff) {------遍歷anon_vma->rb_root紅黑樹中的AVC,從AVC得到相應(yīng)的VMA。
        struct vm_area_struct *vma = avc->vma;
        unsigned long address = vma_address(page, vma);

        if (rwc->invalid_vma && rwc->invalid_vma(vma, rwc->arg))
            continue;

        ret = rwc->rmap_one(page, vma, address, rwc->arg);-----------------------實(shí)際的斷開用戶PTE頁表項(xiàng)操作。
        if (ret != SWAP_AGAIN)
            break;
        if (rwc->done && rwc->done(page))
            break;
    }
    anon_vma_unlock_read(anon_vma);
    return ret;
}

struct rmap_walk_control中的rmap_one實(shí)現(xiàn)是try_to_unmap_one,最終調(diào)用page_remove_rmap()和page_cache_release()來斷開PTE映射關(guān)系。

六、補(bǔ)充:其他反向映射

6.1KSM反向映射

KSM(kernel shared memory)是一種內(nèi)存共享機(jī)制,啟用 ksm后,ksm守護(hù)進(jìn)程會定期掃描用戶內(nèi)存區(qū)域,并合并具有相同內(nèi)存的匿名物理頁面以減少頁面的冗余。

和“多個(gè)子進(jìn)程”不一樣的是,映射到同一個(gè)物理頁面, ksm 在不同進(jìn)程的虛擬地址空間的虛擬地址肯定是不一樣的,因此就不能使用 page->index 來表示虛擬頁號。

對于ksm 頁面,內(nèi)核維護(hù)了一個(gè)結(jié)構(gòu)體 rmap_item,用它來保存同一物理頁面在不同的虛擬地址空間信息;物理頁的 mapping 指向了一個(gè) struct stable_node 結(jié)構(gòu)體,通過 stable_node->hlist 將 rmap_item 連接起來。

rmap_item中維護(hù)了page對應(yīng)的虛擬地址address及anon_vma結(jié)構(gòu)。(函數(shù)實(shí)現(xiàn):mm\rmap.c:page_referenced -> rmap_walk ->rmap_walk_ksm);

圖片圖片

6.2文件頁的反向映射

文件頁的反向映射是指在操作系統(tǒng)中,將虛擬內(nèi)存地址與物理內(nèi)存地址之間進(jìn)行映射的過程。它允許操作系統(tǒng)跟蹤每個(gè)虛擬頁到物理頁的對應(yīng)關(guān)系。

通常,操作系統(tǒng)使用頁表來管理虛擬內(nèi)存和物理內(nèi)存之間的映射關(guān)系。每個(gè)進(jìn)程都有自己獨(dú)立的頁表,通過查閱頁表,操作系統(tǒng)可以將進(jìn)程的虛擬地址轉(zhuǎn)換為對應(yīng)的物理地址。

反向映射則是根據(jù)給定的物理內(nèi)存地址,找到對應(yīng)的虛擬內(nèi)存地址。這樣可以在需要時(shí)快速找到一個(gè)物理頁面所屬的進(jìn)程和虛擬地址。

實(shí)現(xiàn)反向映射通常需要一些數(shù)據(jù)結(jié)構(gòu)支持,比如逆向頁表或者其他類似結(jié)構(gòu)。這樣,在需要時(shí)就可以從給定的物理地址追溯到相應(yīng)的虛擬地址。

圖片圖片

物理頁的 mapping 會指向文件對應(yīng)的 address_space,address_space 的i_mmap 保存了所有的vma。物理頁的所有 vma,很容易找到,那不同vma的虛擬地址呢?

物理頁的index 表示物理頁在文件內(nèi)的offset(以page size為單位),vma的vm_pgoff 表示 vma 區(qū)域在文件中的偏移量。那么,在不同的vma的虛擬地址= page->index - vma->vm_pgoff + vma->vm_start 。

圖片圖片

知道了 vma和 虛擬地址,就可以解除pte映射了,(函數(shù)實(shí)現(xiàn):mm\rmap.c:page_referenced -> rmap_walk ->rmap_walk_file)。

責(zé)任編輯:武曉燕 來源: 深度Linux
相關(guān)推薦

2020-11-20 07:55:55

Linux內(nèi)核映射

2018-01-03 00:38:20

大數(shù)據(jù)Hadoop分布式文件系統(tǒng)

2015-09-11 13:54:51

大數(shù)據(jù)關(guān)鍵技術(shù)

2021-03-03 09:32:21

大數(shù)據(jù)關(guān)鍵技術(shù)數(shù)據(jù)存儲

2018-06-14 09:38:53

Linux多核編程

2025-02-17 09:00:00

DeepSeek人工智能AI

2023-09-20 20:11:07

Java

2018-05-19 00:13:08

2018-05-20 15:43:50

2018-11-21 14:44:33

數(shù)據(jù)庫容器數(shù)據(jù)架構(gòu)

2018-12-04 15:32:09

數(shù)據(jù)處理大數(shù)據(jù)數(shù)據(jù)分析

2018-03-09 12:00:02

數(shù)字化數(shù)據(jù)庫容器

2011-03-21 15:29:46

2016-11-17 18:19:13

VR直播

2015-05-25 17:38:38

4GTD-LTE

2013-07-31 09:19:08

4G網(wǎng)絡(luò)4G牌照4G

2015-09-18 16:42:53

軟件定義數(shù)據(jù)中心

2021-05-17 14:57:22

NFV虛擬化數(shù)據(jù)

2022-04-15 15:03:42

云計(jì)算容器Linux
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號