成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Linux內核反向映射RMAP:加速數據訪問的關鍵技術

系統 Linux
在計算機系統中,內存管理一直是操作系統的核心功能之一,對于 Linux 系統而言,其內存管理機制隨著時間的推移不斷演進,從早期的簡單形式逐漸發展為如今復雜而高效的體系。

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

一、Linux 內存管理的 “前世今生”

在計算機系統中,內存管理一直是操作系統的核心功能之一,對于 Linux 系統而言,其內存管理機制隨著時間的推移不斷演進,從早期的簡單形式逐漸發展為如今復雜而高效的體系。

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

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

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

為應對這一挑戰,在 Linux 2.5 內核開發期間,反向映射(Reverse Mapping,RMAP)的概念被提出并逐步完善。RMAP 的出現,徹底改變 Linux 內存管理中查找頁面映射關系的方式,極大提高內存管理效率,為 Linux 系統在各種復雜環境下穩定高效運行奠定堅實基礎。

二、RMAP是什么?

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

在頁面回收場景中,當系統內存不足時,需要將一些長時間未使用的頁面從物理內存中回收,釋放出空間供其他更急需內存的進程使用。在沒有 RMAP 機制之前,要回收一個物理頁面,內核必須遍歷系統中每個進程的頁表,檢查該物理頁面是否被某個進程映射,這一過程效率極低,因為現代計算機系統中往往運行著大量進程,每個進程又有龐大的頁表,遍歷所有進程頁表的時間開銷巨大。而有了 RMAP 后,內核可以直接通過物理頁面的相關數據結構,快速找到所有映射該頁面的虛擬地址,然后斷開這些映射關系,將頁面回收,大大提高內存回收效率。

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

RMAP 機制對于 Linux 系統的內存管理效率提升至關重要,它是解決復雜內存管理問題的核心方案,為系統在各種內存操作場景下的高效運行提供了有力支持。

2.1正向映射

當進程分配內存并發生寫操作時,會分配虛擬地址并產生缺頁,進而分配物理內存并建立虛擬地址到物理地址的映射關系, 這個叫正向映射。

圖片圖片

2.2反向映射

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

圖片圖片

2.3RMAP的背景

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

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

三、RMAP 關鍵數據結構剖析

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

為了達到這個目的,內核在頁面創建時需要建立RMAP的“鉤子”,即建立相關的數據結構,RMAP系統中有兩個重要的數據結構:一個是anon_vma,簡稱AV;另一個是anon_vma_chain,簡稱AVC。

3.1struct anon_vma(AV)

struct anon_vma 是 RMAP 機制中用于管理匿名內存映射區域的關鍵數據結構,在連接物理頁面的 page 數據結構和虛擬內存區域的 vm_area_struct(VMA) 中扮演著核心角色 。當進程創建匿名內存映射(比如通過 malloc 分配內存,在缺頁異常時創建的匿名頁面)時,anon_vma 就開始發揮作用。

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

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;             /* 引用計數,記錄當前有多少個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 字段用于引用計數,它記錄著當前有多少個 VMA 引用了該 anon_vma。當一個新的 VMA 與該 anon_vma 建立關聯時,refcount 會增加;反之,當 VMA 與 anon_vma 解除關聯時,refcount 減少。當 refcount 變為 0 時,說明沒有 VMA 再引用這個 anon_vma,此時就可以對 anon_vma 進行回收。

rb_root 字段是一個紅黑樹的根節點,通過這個紅黑樹,anon_vma 可以高效地管理和查找與它相關的 VMA。在實際場景中,當系統需要查找所有引用某個匿名頁面的 VMA 時,就可以通過 page 結構中的 mapping 指針找到對應的 anon_vma,然后遍歷 anon_vma 的紅黑樹,快速獲取所有相關的 VMA,大大提高查找效率。

3.2struct anon_vma_chain(AVC)

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

struct anon_vma_chain {
    struct vm_area_struct *vma;         // 指向對應的VMA
    struct anon_vma *anon_vma;         // 指向對應的anon_vma
    struct list_head same_vma;         // 用于鏈接與同一VMA相關的所有anon_vma_chain節點
    struct rb_node rb;                 // 用于將anon_vma_chain節點插入到anon_vma的紅黑樹中
    unsigned long rb_subtree_last;
#ifdef CONFIG_DEBUG_VM_RB
    unsigned long cached_vma_start, cached_vma_last;
#endif
};

在進程創建子進程時,子進程會復制父進程的地址空間和頁表。此時,anon_vma_chain 就會發揮作用,它會在子進程的 VMA 和父進程的 anon_vma 之間建立連接。具體來說,子進程的每個 VMA 都會創建一個 anon_vma_chain 節點,該節點的 vma 指針指向子進程的 VMA,anon_vma 指針指向父進程的 anon_vma,然后通過 same_vma 鏈表和 rb 紅黑樹節點,將這個 anon_vma_chain 節點插入到相應的鏈表和紅黑樹中,從而實現子進程 VMA 與父進程 anon_vma 的關聯 。這樣,當系統需要對某個匿名頁面進行操作時,就可以通過 anon_vma_chain 快速找到所有與該頁面相關的 VMA,無論是在父進程還是子進程中。

3.3struct vm_area_struct(VMA)

struct vm_area_struct 用于描述進程地址空間中的一段虛擬內存區域,是進程虛擬內存管理的重要數據結構,在 RMAP 機制中也有著關鍵作用,它記錄了虛擬內存區域的起始地址、結束地址、訪問權限、所屬的內存描述符等重要信息,與 RMAP 相關的字段主要有 anon_vma_chain 和 anon_vma。

struct vm_area_struct {
    unsigned long vm_start;         // 虛擬內存區域的起始地址
    unsigned long vm_end;           // 虛擬內存區域的結束地址
    struct mm_struct *vm_mm;       // 指向所屬的內存描述符
    struct vm_area_struct *vm_next; // 指向下一個虛擬內存區域
    pgprot_t vm_page_prot;         // 頁面保護標志
    unsigned long vm_flags;        // 虛擬內存區域的標志
    struct list_head anon_vma_chain; // 用于鏈接anon_vma_chain節點,通過這個鏈表可以找到與該VMA相關的所有anon_vma_chain
    struct anon_vma *anon_vma;     // 指向該VMA對應的anon_vma
    // 其他字段...
};

在進程運行過程中,當發生缺頁異常需要創建新的匿名頁面時,內核會為該頁面所在的 VMA 分配一個 anon_vma,并通過 anon_vma_chain 將 VMA 和 anon_vma 連接起來。例如,當一個進程調用 malloc 分配內存時,會在進程的地址空間中創建一個新的 VMA 來管理這塊內存,同時為這個 VMA 關聯一個 anon_vma,并通過 anon_vma_chain 建立兩者之間的聯系。這樣,在后續的內存操作中,如頁面回收、遷移等,就可以通過 VMA 的這些字段快速找到相關的 anon_vma 和其他關聯信息,從而高效地完成內存管理任務。

四、RMAP的工作流程與原理

4.1匿名頁面的創建與 RMAP 初始化

在進程運行過程中,當訪問的虛擬地址尚未映射到物理頁面時,會觸發缺頁異常。此時,內核會調用 do_anonymous_page 函數來處理匿名頁面的創建。以 do_anonymous_page 函數為例,當進程調用 malloc 分配內存時,如果相應的虛擬地址尚未映射物理頁面,就會觸發缺頁異常,進而調用 do_anonymous_page 函數。在這個函數中,會先后調用 anon_vma_prepare 和 page_add_new_anon_rmap 函數來完成 RMAP 相關的初始化工作。

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;

    // 準備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 函數主要負責為 VMA 分配和初始化 anon_vma 結構,并建立 anon_vma_chain 連接。在這個函數中,首先嘗試查找是否存在可合并的 anon_vma,如果不存在則分配一個新的 anon_vma。然后,通過 anon_vma_chain_alloc 分配一個 anon_vma_chain 結構,并將其與 VMA 和 anon_vma 進行關聯。具體來說,它會將 anon_vma_chain 的 vma 指針指向 VMA,anon_vma 指針指向分配的 anon_vma,然后將 anon_vma_chain 添加到 VMA 的 anon_vma_chain 鏈表和 anon_vma 的紅黑樹中,這樣就建立了 VMA 和 anon_vma 之間的雙向關聯。

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);
    }

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

    return 0;
}

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

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子進程創建時的 RMAP 處理

當父進程通過 fork 創建子進程時,子進程需要復制父進程的地址空間及頁表。在這個過程中,dup_mmap 函數負責復制父進程的進程地址空間,遍歷父進程的 VMA 鏈表,為每個 VMA 創建一個新的 VMA 數據結構,并將父進程 VMA 中的相關信息復制到子進程的 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) {
        // 創建臨時VMA數據結構tmp,把父進程VMA復制到子進程剛創建的tmp中
        tmp = vm_area_dup(mpnt);
        if (!tmp) {
            retval = -ENOMEM;
            goto free_vmas;
        }

        tmp->vm_mm = mm;

        // 為子進程創建相應anon_vma數據結構并添加到紅黑樹
        if (anon_vma_fork(tmp, mpnt)) {
            __vma_link_rb(mm, tmp, rb_link, rb_parent);
        }

        // 復制父進程的PTE到子進程
        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:
    // 釋放相關資源
    return retval;
}

anon_vma_fork 函數在子進程創建過程中起著關鍵作用,負責為子進程的 VMA 創建相應的 anon_vma 數據結構,并建立子進程 VMA 與父進程 anon_vma 之間的關聯。如果父進程的 VMA 沒有關聯的 anon_vma,則直接返回。否則,嘗試克隆父進程的 anon_vma 到子進程。如果克隆失敗,則分配一個新的 anon_vma 給子進程,并將其與子進程的 VMA 通過 anon_vma_chain 連接起來,同時設置相關的父子關系和引用計數 。

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;
}

在實際場景中,假設父進程有一個 VMA 用于管理堆內存,當通過 fork 創建子進程時,子進程會復制父進程的這個 VMA 結構,并通過 anon_vma_fork 函數建立與父進程 anon_vma 的關聯。這樣,在后續內存操作中,系統可以通過 RMAP 機制統一管理父子進程中與該 VMA 相關的匿名頁面,實現內存的高效利用和共享。

4.3頁面回收與遷移時的 RMAP 應用

當系統內存不足時,kswapd 內核線程會啟動頁面回收機制,查找可以回收的頁面。對于匿名頁面,kswapd 會利用 RMAP 機制來找到所有映射該頁面的 PTE 并斷開映射。具體來說,kswapd 會調用 try_to_unmap 函數,該函數是反向映射的核心函數之一,它會遍歷頁面的反向映射關系,找到所有映射該頁面的 VMA,并調用 try_to_unmap_one 函數來斷開每個 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;
}

在頁面遷移場景中,當需要將一個物理頁面從一個內存區域遷移到另一個內存區域時,同樣需要利用 RMAP 機制。例如在 NUMA 架構系統中,為了優化內存訪問性能,可能需要將某個進程使用的頁面遷移到距離其 CPU 更近的內存節點上。在遷移過程中,首先通過 RMAP 找到所有映射該頁面的 PTE,然后將這些 PTE 標記為無效,并在目標內存區域分配新的物理頁面。遷移完成后,再更新 PTE 的映射關系,指向新的物理頁面 。整個過程中,RMAP 機制確保了在頁面遷移前后,所有相關進程的頁表能夠正確更新,保證進程對內存的正常訪問。

五、反向映射RMAP應用

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

  1. kswapd內核線程回收頁面需要斷開所有映射了該匿名頁面的用戶PTE頁表項。
  2. 頁面遷移時,需要斷開所有映射到匿名頁面的用戶PTE頁表項。

try_to_unmap()是反向映射的核心函數,內核中其他模塊會調用此函數來斷開一個頁面的所有映射:

/**
 * 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---------------可能錯過了一個映射的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,--------------------------------具體斷開某個VMA上映射的pte
        .arg = (void *)flags,
        .done = page_not_mapped,-------------------------------------判斷一個頁面是否斷開成功的條件
        .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;
}

內核中有三種頁面需要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數據結構,并申請一個讀者鎖。
    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得到相應的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);-----------------------實際的斷開用戶PTE頁表項操作。
        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實現是try_to_unmap_one,最終調用page_remove_rmap()和page_cache_release()來斷開PTE映射關系。

六、補充:其他反向映射

6.1KSM反向映射

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

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

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

rmap_item中維護了page對應的虛擬地址address及anon_vma結構。(函數實現:mm\rmap.c:page_referenced -> rmap_walk ->rmap_walk_ksm);

圖片圖片

6.2文件頁的反向映射

文件頁的反向映射是指在操作系統中,將虛擬內存地址與物理內存地址之間進行映射的過程。它允許操作系統跟蹤每個虛擬頁到物理頁的對應關系。

通常,操作系統使用頁表來管理虛擬內存和物理內存之間的映射關系。每個進程都有自己獨立的頁表,通過查閱頁表,操作系統可以將進程的虛擬地址轉換為對應的物理地址。

反向映射則是根據給定的物理內存地址,找到對應的虛擬內存地址。這樣可以在需要時快速找到一個物理頁面所屬的進程和虛擬地址。

實現反向映射通常需要一些數據結構支持,比如逆向頁表或者其他類似結構。這樣,在需要時就可以從給定的物理地址追溯到相應的虛擬地址。

圖片圖片

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

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

圖片圖片

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

責任編輯:武曉燕 來源: 深度Linux
相關推薦

2020-11-20 07:55:55

Linux內核映射

2018-01-03 00:38:20

大數據Hadoop分布式文件系統

2015-09-11 13:54:51

大數據關鍵技術

2021-03-03 09:32:21

大數據關鍵技術數據存儲

2018-06-14 09:38:53

Linux多核編程

2023-09-20 20:11:07

Java

2025-02-17 09:00:00

DeepSeek人工智能AI

2018-05-19 00:13:08

2018-05-20 15:43:50

2018-11-21 14:44:33

數據庫容器數據架構

2018-12-04 15:32:09

數據處理大數據數據分析

2018-03-09 12:00:02

數字化數據庫容器

2011-03-21 15:29:46

2013-07-31 09:19:08

4G網絡4G牌照4G

2015-05-25 17:38:38

4GTD-LTE

2017-07-12 13:49:45

微服務架構數據共享

2018-03-27 09:10:52

AI

2023-04-04 10:33:07

自動駕駛

2022-04-15 15:03:42

云計算容器Linux
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产三级电影网站 | 久草福利 | 精彩视频一区二区三区 | 日韩1区| 99re在线视频观看 | 精品一区二区三区免费毛片 | 国外成人在线视频网站 | 视频一区二区三区中文字幕 | 免费v片在线观看 | 亚洲视频中文字幕 | 国产精品美女久久久免费 | 欧美国产中文字幕 | 红色av社区| 欧美区在线 | 欧美久久久网站 | 久热国产在线 | 欧美99| 天天草天天爱 | 久久在线 | 国产成人99久久亚洲综合精品 | 欧美亚洲国产一区二区三区 | 欧美久久电影 | 久热久热 | 看a级黄色毛片 | 91精品国产一二三 | 国产一伦一伦一伦 | 免费视频久久 | 国产福利在线播放 | 波波电影院一区二区三区 | 日韩在线小视频 | 中文字幕国产 | 国产成人精品亚洲日本在线观看 | 日韩成人av在线 | 日本免费一区二区三区四区 | 久久精品久久精品 | 亚洲成人一区二区 | 在线观看 亚洲 | 日韩一区欧美一区 | 亚洲精品国产第一综合99久久 | 欧美1页| 日韩国产欧美在线观看 |