精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

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

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

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

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

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

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

為了解決這些問題,虛擬內(nèi)存的概念應(yīng)運而生。虛擬內(nèi)存作為程序和物理內(nèi)存之間的中間層,為每個進(jìn)程提供獨立的地址空間,實現(xiàn)進(jìn)程地址空間的隔離,增強(qiáng)系統(tǒng)安全性。在 Linux 系統(tǒng)中,虛擬內(nèi)存通過分頁和分段技術(shù)實現(xiàn)虛擬地址到物理地址的映射。分段技術(shù)將程序地址空間劃分為不同邏輯段,如代碼段、數(shù)據(jù)段、棧段等,每個段在物理內(nèi)存中可以不連續(xù)存儲,解決程序運行地址不確定問題,但仍存在外部碎片問題。分頁技術(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)需要確定某物理頁面是否被某個進(jìn)程映射時,必須遍歷每個進(jìn)程的頁表,這一過程工作量巨大,效率極低。比如在一個擁有大量進(jìn)程的服務(wù)器系統(tǒng)中,若要查找某個物理頁面的映射關(guān)系,遍歷所有進(jìn)程頁表可能需要耗費大量 CPU 時間,嚴(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)定高效運行奠定堅實基礎(chǔ)。

二、RMAP是什么?

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

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

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

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

2.1正向映射

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

圖片圖片

2.2反向映射

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

圖片圖片

2.3RMAP的背景

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

在Linux 2.4內(nèi)核中,為了確定某一個頁面是否被某個進(jìn)程映射,必須遍歷每個進(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,這樣頁面回收模塊就可以很快速和高效地把這個物理頁面映射的所有用戶PTE都解除并回收這個頁面。在深入了解 RMAP 的工作原理之前,我們先來剖析一下支撐 RMAP 機(jī)制運行的關(guān)鍵數(shù)據(jù)結(jié)構(gòu),它們是 RMAP 實現(xiàn)高效反向映射的基石,理解這些數(shù)據(jù)結(jié)構(gòu)的構(gòu)成和相互關(guān)系,對于掌握 RMAP 的核心思想至關(guān)重要。

為了達(dá)到這個目的,內(nèi)核在頁面創(chuàng)建時需要建立RMAP的“鉤子”,即建立相關(guān)的數(shù)據(jù)結(jié)構(gòu),RMAP系統(tǒng)中有兩個重要的數(shù)據(jù)結(jié)構(gòu):一個是anon_vma,簡稱AV;另一個是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)存,在缺頁異常時創(chuàng)建的匿名頁面)時,anon_vma 就開始發(fā)揮作用。

從定義來看,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;             /* 引用計數(shù),記錄當(dāng)前有多少個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 字段用于引用計數(shù),它記錄著當(dāng)前有多少個 VMA 引用了該 anon_vma。當(dāng)一個新的 VMA 與該 anon_vma 建立關(guān)聯(lián)時,refcount 會增加;反之,當(dāng) VMA 與 anon_vma 解除關(guān)聯(lián)時,refcount 減少。當(dāng) refcount 變?yōu)?0 時,說明沒有 VMA 再引用這個 anon_vma,此時就可以對 anon_vma 進(jìn)行回收。

rb_root 字段是一個紅黑樹的根節(jié)點,通過這個紅黑樹,anon_vma 可以高效地管理和查找與它相關(guān)的 VMA。在實際場景中,當(dāng)系統(tǒng)需要查找所有引用某個匿名頁面的 VMA 時,就可以通過 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é)點
    struct rb_node rb;                 // 用于將anon_vma_chain節(jié)點插入到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)程時,子進(jìn)程會復(fù)制父進(jìn)程的地址空間和頁表。此時,anon_vma_chain 就會發(fā)揮作用,它會在子進(jìn)程的 VMA 和父進(jìn)程的 anon_vma 之間建立連接。具體來說,子進(jìn)程的每個 VMA 都會創(chuàng)建一個 anon_vma_chain 節(jié)點,該節(jié)點的 vma 指針指向子進(jìn)程的 VMA,anon_vma 指針指向父進(jìn)程的 anon_vma,然后通過 same_vma 鏈表和 rb 紅黑樹節(jié)點,將這個 anon_vma_chain 節(jié)點插入到相應(yīng)的鏈表和紅黑樹中,從而實現(xiàn)子進(jìn)程 VMA 與父進(jìn)程 anon_vma 的關(guān)聯(lián) 。這樣,當(dāng)系統(tǒng)需要對某個匿名頁面進(jìn)行操作時,就可以通過 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; // 指向下一個虛擬內(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é)點,通過這個鏈表可以找到與該VMA相關(guān)的所有anon_vma_chain
    struct anon_vma *anon_vma;     // 指向該VMA對應(yīng)的anon_vma
    // 其他字段...
};

在進(jìn)程運行過程中,當(dāng)發(fā)生缺頁異常需要創(chuàng)建新的匿名頁面時,內(nèi)核會為該頁面所在的 VMA 分配一個 anon_vma,并通過 anon_vma_chain 將 VMA 和 anon_vma 連接起來。例如,當(dāng)一個進(jìn)程調(diào)用 malloc 分配內(nèi)存時,會在進(jìn)程的地址空間中創(chuàng)建一個新的 VMA 來管理這塊內(nèi)存,同時為這個 VMA 關(guān)聯(lián)一個 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)程運行過程中,當(dāng)訪問的虛擬地址尚未映射到物理頁面時,會觸發(fā)缺頁異常。此時,內(nèi)核會調(diào)用 do_anonymous_page 函數(shù)來處理匿名頁面的創(chuàng)建。以 do_anonymous_page 函數(shù)為例,當(dāng)進(jìn)程調(diào)用 malloc 分配內(nèi)存時,如果相應(yīng)的虛擬地址尚未映射物理頁面,就會觸發(fā)缺頁異常,進(jìn)而調(diào)用 do_anonymous_page 函數(shù)。在這個函數(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 連接。在這個函數(shù)中,首先嘗試查找是否存在可合并的 anon_vma,如果不存在則分配一個新的 anon_vma。然后,通過 anon_vma_chain_alloc 分配一個 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,并計算頁面在 VMA 中的索引值存儲在 index 字段中。具體實現(xiàn)中,先設(shè)置頁面的 SwapBacked 標(biāo)志,表示該頁面可交換到磁盤。然后根據(jù)頁面是否為復(fù)合頁(如大頁),設(shè)置 mapcount 字段。接著,通過 __page_set_anon_rmap 函數(shù)將 anon_vma 加上 PAGE_MAPPING_ANON 后賦值給頁面的 mapping 字段,并計算頁面在 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)建時的 RMAP 處理

當(dāng)父進(jìn)程通過 fork 創(chuàng)建子進(jìn)程時,子進(jìn)程需要復(fù)制父進(jìn)程的地址空間及頁表。在這個過程中,dup_mmap 函數(shù)負(fù)責(zé)復(fù)制父進(jìn)程的進(jìn)程地址空間,遍歷父進(jìn)程的 VMA 鏈表,為每個 VMA 創(chuàng)建一個新的 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)建臨時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)程。如果克隆失敗,則分配一個新的 anon_vma 給子進(jìn)程,并將其與子進(jìn)程的 VMA 通過 anon_vma_chain 連接起來,同時設(shè)置相關(guān)的父子關(guān)系和引用計數(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è)父進(jìn)程有一個 VMA 用于管理堆內(nèi)存,當(dāng)通過 fork 創(chuàng)建子進(jìn)程時,子進(jìn)程會復(fù)制父進(jìn)程的這個 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)的匿名頁面,實現(xiàn)內(nèi)存的高效利用和共享。

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

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

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

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

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

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

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

內(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),并申請一個讀者鎖。
    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);-----------------------實際的斷開用戶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實現(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)存的匿名物理頁面以減少頁面的冗余。

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

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

rmap_item中維護(hù)了page對應(yīng)的虛擬地址address及anon_vma結(jié)構(gòu)。(函數(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)跟蹤每個虛擬頁到物理頁的對應(yīng)關(guān)系。

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

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

實現(xiàn)反向映射通常需要一些數(shù)據(jù)結(jié)構(gòu)支持,比如逆向頁表或者其他類似結(jié)構(gòu)。這樣,在需要時就可以從給定的物理地址追溯到相應(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ù)實現(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-20 15:43:50

2018-05-19 00:13:08

2018-11-21 14:44:33

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

2018-03-09 12:00:02

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

2018-12-04 15:32:09

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

2011-03-21 15:29:46

2013-07-31 09:19:08

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

2015-05-25 17:38:38

4GTD-LTE

2016-11-17 18:19:13

VR直播

2021-05-17 14:57:22

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

2022-04-15 15:03:42

云計算容器Linux

2015-09-18 16:42:53

軟件定義數(shù)據(jù)中心
點贊
收藏

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

久久久久久黄| 日韩美女毛片| 亚洲综合精品久久| 久久一区免费| 亚洲熟女乱色一区二区三区久久久| 99热国内精品永久免费观看| 亚洲第一免费网站| 999精品视频在线| www在线观看播放免费视频日本| 国产成人在线网站| 欧美最猛性xxxx| 91精品国产高清一区二区三蜜臀| 婷婷精品在线| 日韩视频国产视频| 亚洲不卡视频在线| 国产黄大片在线观看| 亚洲欧洲国产日本综合| 欧美激情视频一区二区三区| 午夜精品久久久久久久第一页按摩| 免费在线成人| 久久久久一本一区二区青青蜜月| 一本一本久久a久久| 日韩午夜av| 欧美第一区第二区| 天天操天天爽天天射| 91福利在线尤物| 国产精品的网站| 欧美性色黄大片人与善| 亚洲xxxx在线| 日韩av片免费观看| 中文字幕乱码在线人视频| 国产剧情久久久| 视频一区二区三区入口| 97视频免费看| 免费在线看黄网址| 欧美~级网站不卡| www国产精品视频| 快灬快灬一下爽蜜桃在线观看| 久久99偷拍| 日韩精品影音先锋| 一卡二卡三卡四卡五卡| 国产一区二区三区免费观看在线| 欧美视频第二页| 已婚少妇美妙人妻系列| 美女写真久久影院| 岛国av一区二区在线在线观看| 日韩黄色短视频| xxxx在线视频| 亚洲第一狼人社区| 僵尸世界大战2 在线播放| 国产色婷婷在线| 亚洲五码中文字幕| www国产精品内射老熟女| 91破解版在线观看| 黑人巨大精品欧美一区二区三区 | 黄色美女一级片| 国产精品亚洲一区二区三区妖精 | 亚洲精品喷潮一区二区三区| 国产毛片精品国产一区二区三区| 成人免费午夜电影| 国产激情久久久久久熟女老人av| 国产成人亚洲综合色影视| 91手机在线观看| 亚洲精品久久久久久久久久 | 国产精品麻豆免费版| 日本免费网站在线观看| 91在线国产福利| 噜噜噜噜噜久久久久久91| 欧美18xxxxx| 国产欧美精品日韩区二区麻豆天美| 日韩欧美亚洲v片| 又爽又大又黄a级毛片在线视频| 国产精品久线观看视频| 亚洲免费av网| 丁香影院在线| 色综合久久66| 亚洲精品20p| 91精品入口| 亚洲精品色婷婷福利天堂| 中国女人特级毛片| 中文字幕免费一区二区| 97视频人免费观看| 中文字幕一区二区三区波野结 | 成人性色av| 污污视频在线免费看| 日本一区二区综合亚洲| 视色,视色影院,视色影库,视色网| 久久大胆人体| 91国偷自产一区二区开放时间 | 中文久久乱码一区二区| 日韩在线视频在线| 天天免费亚洲黑人免费| 欧美一级视频精品观看| 色天使在线视频| 99久久99热这里只有精品| 性色av香蕉一区二区| 中文字幕+乱码+中文乱码www| 国产成人鲁色资源国产91色综| 欧美日韩国产精品一卡| 午夜羞羞小视频在线观看| 一本一道综合狠狠老| 成人免费播放视频| 欧美日韩国产在线观看网站| 欧美激情视频一区| 中文字幕在线2018| 91在线你懂得| 成人小视频在线观看免费| 蜜桃视频成人m3u8| 亚洲国产高清福利视频| 国产亚洲精品久久久久久豆腐| 国产日韩一区二区三区在线| 91免费电影网站| 国产福利在线| 欧美日韩亚洲精品内裤| 四虎国产精品免费| 国产精品麻豆久久| 奇米4444一区二区三区| 人妻中文字幕一区| 亚洲视频狠狠干| 欧美性猛交久久久乱大交小说| 国产精品对白久久久久粗| 久久久黄色av| 最新中文字幕免费| 久久久久久毛片| 久久综合色视频| 日韩成人在线看| 中文字幕在线亚洲| 国产一区免费看| 91丨九色丨黑人外教| 欧美激情亚洲天堂| 亚洲精品一区二区三区在线| 久久精品国产一区二区电影| 怡春院在线视频| 久久久高清一区二区三区| 欧美大黑帍在线播放| 欧美日韩黄色| 欧美成人剧情片在线观看| 国产精品日韩无码| 综合在线观看色| 亚洲欧美日韩精品一区| 日韩在线高清| 国产精品视频播放| 无遮挡的视频在线观看| 欧美日韩久久一区二区| www.xx日本| 国产在线麻豆精品观看| 99亚洲国产精品| 欧美日本三级| 国内精品久久久久久久久| 丰满少妇在线观看bd| 亚洲成人精品在线观看| 国产 xxxx| 亚洲欧美网站| 日本中文不卡| 美女视频一区| 久久综合久中文字幕青草| aaa一区二区| 亚洲国产成人av网| 亚洲国产精品成人综合久久久| 久久精品官网| 四虎影视永久免费在线观看一区二区三区| 国产一区二区主播在线| 日韩一区二区福利| 精品二区在线观看| 性做久久久久久| 一级片手机在线观看| 免费的国产精品| 在线观看三级网站| 国产欧美自拍一区| 国内免费久久久久久久久久久 | 久久精品久久久久久国产 免费| 国产伦子伦对白视频| 亚洲国产精品久久不卡毛片| 性色av蜜臀av色欲av| 美女尤物国产一区| 强开小嫩苞一区二区三区网站 | 亚洲护士老师的毛茸茸最新章节| 免费观看一区二区三区毛片| 国产女人aaa级久久久级 | 色综合久久悠悠| 天天干天天做天天操| 在线亚洲一区二区| 日本精品人妻无码77777| 96av麻豆蜜桃一区二区| 在线观看免费视频高清游戏推荐| 欧美日韩影院| 日韩亚洲欧美精品| 澳门精品久久国产| 国产精品男女猛烈高潮激情| 手机在线免费观看av| 亚洲最新中文字幕| 亚洲国产视频一区二区三区| 色94色欧美sute亚洲线路一久| 色在线观看视频| 国产亚洲综合在线| 动漫av在线免费观看| 久久一二三四| 青青青在线视频播放| 99re66热这里只有精品8| 国产伦理久久久| 午夜不卡一区| 欧美中文字幕精品| 男人添女人下部高潮视频在线观看| 国产一区二区三区在线播放免费观看| 国内精品久久久久久久久久| 欧美综合久久久| 日韩 国产 在线| 亚洲日韩欧美一区二区在线| 最近中文字幕免费| 成人av免费在线观看| 不用播放器的免费av| 视频一区二区国产| 欧美激情 国产精品| 欧美在线日韩| 亚洲国产日韩美| 中文字幕伦av一区二区邻居| 国产麻豆日韩| 亚洲日本视频在线| 成人午夜在线观看| 欧美成人福利| 国产精品久久久久久久久借妻 | 在线播放毛片| 夜夜嗨av色一区二区不卡| 三级在线播放| 亚洲精品456在线播放狼人| 国产夫妻在线观看| 制服丝袜av成人在线看| 又骚又黄的视频| 色菇凉天天综合网| 国产主播第一页| 色偷偷久久一区二区三区| xxxx.国产| 天天操天天综合网| 日本特黄特色aaa大片免费| 一区二区三区在线观看欧美| 91高清免费看| 亚洲欧美日韩精品久久久久| 波多野结衣欲乱| 国产精品久久久久久久久免费相片 | 中文字幕亚洲欧美日韩高清| 国产免费a∨片在线观看不卡| 日韩乱码在线视频| 欧美人体大胆444www| 亚洲美女av电影| 欧美日韩伦理片| 亚洲欧美日韩国产成人| 蜜桃视频在线免费| 亚洲午夜激情免费视频| 九色在线视频蝌蚪| 国产亚洲一区二区精品| 福利视频在线看| 日韩专区在线播放| 国产精品一区二区三区视频网站| 另类专区欧美制服同性| 成人影院www在线观看| 欧美日韩国产999| sm久久捆绑调教精品一区| 国内成人精品视频| 成人激情综合| 国产一区视频在线| 亚洲精品国产九九九| 狠狠色噜噜狠狠色综合久| 亚洲精品亚洲人成在线| 日韩精品久久一区二区三区| 99久久影视| 国产高清www| 久久久久国产精品一区三寸| av污在线观看| 国产成人精品免费| 亚洲精品成人无码熟妇在线| 国产精品色一区二区三区| 久久久久久视频| 欧美日韩精品二区| 中文字幕 欧美激情| 精品少妇一区二区三区在线播放| 人妻无码中文字幕| 丝袜美腿精品国产二区| 污污的网站在线免费观看| 91精品国产免费久久久久久| 国产成人精选| 国产91色在线|亚洲| 精品视频网站| 超碰人人爱人人| 首页亚洲欧美制服丝腿| 香蕉视频色在线观看| 久久婷婷久久一区二区三区| 69夜色精品国产69乱| 婷婷综合久久一区二区三区| 中文字幕人妻互换av久久| 欧美sm极限捆绑bd| 天堂资源在线中文| 91国产一区在线| www一区二区三区| 欧美成ee人免费视频| 午夜精品婷婷| 午夜激情福利在线| 97精品国产露脸对白| 日韩精品一区二区亚洲av性色| 欧美日韩一区二区免费视频| 国产乱码一区二区| 亚洲视频综合网| av伦理在线| 亚洲xxxxx电影| 欧美日韩在线网站| 天天夜碰日日摸日日澡性色av| 久久成人免费网| 国精产品一区一区三区免费视频| 亚洲综合图片区| 亚洲午夜激情视频| 亚洲激情五月| 国产91一区二区三区| 日韩精品1区| 久久精品99国产| 成人午夜视频在线| 三级影片在线看| 欧美日韩精品一区二区三区| 久久免费看视频| 97视频网站入口| 电影一区二区在线观看| 91看片淫黄大片91| 国产剧情在线观看一区二区| 国产欧美一区二区三区在线观看视频| 精品福利在线视频| 欧美一区,二区| 欧美俄罗斯性视频| 麻豆国产精品| 免费成人深夜夜行网站视频| 麻豆一区二区三| 成人黄色a级片| 色狠狠色狠狠综合| 国产女人在线视频| 国产精品久久久久国产a级| 亚洲女娇小黑人粗硬| 99久久国产综合精品五月天喷水| 国产成人8x视频一区二区| 美女的奶胸大爽爽大片| 欧美一区二区三区免费| wwwav在线| 亚洲va欧美va在线观看| 午夜精品久久99蜜桃的功能介绍| 18深夜在线观看免费视频| 亚洲激情自拍偷拍| 亚洲h视频在线观看| 欧美俄罗斯乱妇| 国产精品x8x8一区二区| 国产黄视频在线| 久久无码av三级| 国产精品欧美综合| 最近2019中文字幕一页二页| 日韩午夜视频在线| mm131午夜| 成人av网址在线| 亚洲熟女综合色一区二区三区| 国产视频亚洲视频| 456成人影院在线观看| 亚洲欧洲精品在线| 国产在线观看免费一区| 精品在线视频免费| 国产偷亚洲偷欧美偷精品| 欧美××××黑人××性爽 | 在线观看免费视频黄| 欧美日韩国产黄| 国产福利免费在线观看| 国产精品女人网站| 综合色一区二区| 成人做爰www看视频软件| 一本色道a无线码一区v| 色的视频在线免费看| 亚洲精品免费av| 99riav国产精品| 天天躁日日躁aaaa视频| 欧美三级在线播放| 香蕉成人app免费看片| 精品免费一区二区三区蜜桃| 日韩黄色免费电影| 少妇久久久久久被弄高潮| 精品视频偷偷看在线观看| 欧美亚洲福利| www.av片| 中文字幕一区二区三区不卡| 黑人精品一区二区三区| 国产91精品在线播放| 欧美~级网站不卡| 日韩精品电影一区二区| 欧美一二三区在线观看| 欧美电影免费观看| 日韩精品一区二区三区四| 国产亚洲午夜高清国产拍精品| 99久久久国产精品无码网爆| 欧美在线视频在线播放完整版免费观看 | 日韩中文字幕一区二区三区| 一区视频免费观看| 亚洲视频视频在线| 大奶在线精品| 中文字幕国产免费| 欧美日韩精品在线观看| a毛片在线播放| 色视频一区二区三区| 成人午夜视频福利| 97久久人国产精品婷婷|