色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

一文詳解linux的分頁(yè)模型

Linux內(nèi)核補(bǔ)給站 ? 來(lái)源:Linux內(nèi)核補(bǔ)給站 ? 作者:Linux內(nèi)核補(bǔ)給站 ? 2022-05-18 08:59 ? 次閱讀

分段和分頁(yè)

先看一幅圖


poYBAGKDjsuAXqOtAAAdwzNAkzY770.jpg?source=d16d100b

?


也就是我們實(shí)際中編碼時(shí)遇到的內(nèi)存地址并不是對(duì)應(yīng)于實(shí)際內(nèi)存上的地址,我們編碼中使用的地址是一個(gè)邏輯地址,會(huì)通過(guò)分段和分頁(yè)這兩個(gè)機(jī)制把它轉(zhuǎn)為物理地址。而由于linux使用的分段機(jī)制有限,可以認(rèn)為,linux下的邏輯地址=線性地址。也就是,我們編碼使用的是線性地址,之后只需要經(jīng)過(guò)一個(gè)分頁(yè)機(jī)制就可以把這個(gè)地址轉(zhuǎn)為物理地址了。所以我們更重要的可能是去說(shuō)明一下linux的分頁(yè)模型。


系統(tǒng)會(huì)將整個(gè)物理內(nèi)存分為多個(gè)頁(yè)框,每個(gè)頁(yè)框大小一般是4K(硬件允許的擴(kuò)展分頁(yè)(PSE)情況下也可設(shè)置為4M,不過(guò)linux并不使用PSE,而可能使用PAE),也就是如果我們有1GB的物理內(nèi)存,系統(tǒng)就會(huì)將這個(gè)物理內(nèi)存分為262144個(gè)頁(yè)框。當(dāng)我們提供一個(gè)線性地址時(shí),系統(tǒng)就會(huì)通過(guò)分頁(yè)機(jī)制將這個(gè)線性地址轉(zhuǎn)換為對(duì)應(yīng)于某個(gè)物理頁(yè)中的某個(gè)內(nèi)存地址。下圖是linux的分頁(yè)模型


pYYBAGKDjsuAU_wUAABdXJyxHGk134.jpg?source=d16d100b

?

linux采用四級(jí)分頁(yè)模型,這四種頁(yè)表是:頁(yè)全局目錄(PGD)、頁(yè)上級(jí)目錄(PUD)、頁(yè)中間目錄(PMD)、頁(yè)表(PTE)。這里的所有頁(yè)全局目錄、頁(yè)上級(jí)目錄、頁(yè)中間目錄、頁(yè)表,它們的大小都是一個(gè)頁(yè)。linux下各個(gè)硬件上并不一定都是使用四級(jí)目錄的,當(dāng)使用于沒有啟動(dòng)物理地址擴(kuò)展(PAE)的32位系統(tǒng)上時(shí),只使用二級(jí)頁(yè)表,linux會(huì)把頁(yè)上級(jí)目錄和頁(yè)中間目錄置空。而在啟用了物理地址擴(kuò)展的32位系統(tǒng)上時(shí),linux使用的是三級(jí)頁(yè)表,頁(yè)上級(jí)目錄被置空。而在64位系統(tǒng)上,linux根據(jù)硬件的情況會(huì)選擇三級(jí)頁(yè)表或者四級(jí)頁(yè)表。這個(gè)整個(gè)由線性地址轉(zhuǎn)換到物理地址的過(guò)程,是由CPU自動(dòng)進(jìn)行的。

每個(gè)進(jìn)程都有它自己的頁(yè)全局目錄,當(dāng)進(jìn)程運(yùn)行時(shí),系統(tǒng)會(huì)將該進(jìn)程的頁(yè)全局目錄基地址保存到cr3寄存器中;而當(dāng)進(jìn)程被換出時(shí),會(huì)將這個(gè)cr3保存的頁(yè)全局目錄地址保存到進(jìn)程描述符中。之后我們還會(huì)介紹一個(gè)cr2寄存器,用于缺頁(yè)異常處理的。當(dāng)進(jìn)程運(yùn)行時(shí),它使用的是它自己的一套頁(yè)表,當(dāng)它通過(guò)系統(tǒng)調(diào)用或陷入內(nèi)核態(tài)時(shí),使用的是內(nèi)核頁(yè)表,實(shí)際上,對(duì)于所有的進(jìn)程頁(yè)表來(lái)說(shuō),它們的線性地址0xC0000000以上所涉及到的頁(yè)表都是主內(nèi)核頁(yè)全局目錄(保存在init_mm.pgd),它們的內(nèi)容等于主內(nèi)核頁(yè)全局目錄的相應(yīng)表項(xiàng),這樣就實(shí)現(xiàn)了所有進(jìn)程的進(jìn)程空間相互隔離,但是內(nèi)核空間相互共享的情況。當(dāng)某個(gè)進(jìn)程修改了內(nèi)核頁(yè)表的一些映射情況后,系統(tǒng)只會(huì)相應(yīng)的修改主內(nèi)核頁(yè)全局目錄中的表項(xiàng)(只能修改高端內(nèi)存中非連續(xù)內(nèi)存區(qū)的映射),當(dāng)其他進(jìn)程訪問(wèn)這些線性地址時(shí),會(huì)出現(xiàn)缺頁(yè)異常,然后修改該進(jìn)程的頁(yè)表項(xiàng)重新映射該地址。

因?yàn)檎f(shuō)到每個(gè)進(jìn)程都有它自己的頁(yè)全局目錄,如果有100個(gè)進(jìn)程,內(nèi)存中就要保存100個(gè)進(jìn)程的整個(gè)頁(yè)表集,看起來(lái)會(huì)耗費(fèi)相當(dāng)多的內(nèi)存。實(shí)際上,只有進(jìn)程使用到的情況下系統(tǒng)才會(huì)分配給進(jìn)程一條路徑,比如我們要求訪問(wèn)一個(gè)線性地址,但是這個(gè)地址可能對(duì)應(yīng)的頁(yè)上級(jí)目錄、頁(yè)中間目錄、頁(yè)表和頁(yè)都不存在的,這時(shí)系統(tǒng)會(huì)產(chǎn)生一個(gè)缺頁(yè)異常,在缺頁(yè)異常處理中再給進(jìn)程的這個(gè)線性地址分配頁(yè)上級(jí)目錄、頁(yè)中間目錄、頁(yè)表和頁(yè)所需的物理頁(yè)框。

地址空間

一個(gè)線性地址經(jīng)過(guò)分頁(yè)機(jī)制轉(zhuǎn)為一個(gè)對(duì)應(yīng)的物理地址,我們稱之為映射,比如我們的一個(gè)線性地址0x00000001經(jīng)過(guò)分頁(yè)機(jī)制處理后,對(duì)應(yīng)的物理地址可能是0xffffff01。

在linux系統(tǒng)中分兩個(gè)地址空間,一個(gè)是進(jìn)程地址空間,一個(gè)是內(nèi)核地址空間。對(duì)于每個(gè)進(jìn)程來(lái)說(shuō),他們都有自己的大小為3G的進(jìn)程地址空間,這些進(jìn)程地址空間是相互隔離的,也就是進(jìn)程A的0x00000001線性地址和進(jìn)程B的0x00000001線性地址并不是同一個(gè)地址,進(jìn)程A也不能通過(guò)自己的進(jìn)程空間直接訪問(wèn)進(jìn)程B的進(jìn)程地址空間。而當(dāng)線性地址大于3G時(shí)(也就是0xC0000000),這里的線性地址屬于內(nèi)核空間,內(nèi)核地址空間的大小為1G,地址從0xC0000000到0xFFFFFFFF。在內(nèi)核地址空間中,內(nèi)核會(huì)把前896MB的線性地址直接與物理地址的前896MB進(jìn)行映射,也就是說(shuō),內(nèi)核地址空間的線性地址0xC0000001所對(duì)應(yīng)的物理地址為0x00000001,它們之間相差一個(gè)0xC0000000。

linux內(nèi)核會(huì)將物理內(nèi)存分為3個(gè)管理區(qū),分別是:

ZONE_DMA:包含0MB~16MB之間的內(nèi)存頁(yè)框,可以由老式基于ISA的設(shè)備通過(guò)DMA使用,直接映射到內(nèi)核的地址空間。

ZONE_NORMAL:包含16MB~896MB之間的內(nèi)存頁(yè)框,常規(guī)頁(yè)框,直接映射到內(nèi)核的地址空間。

ZONE_HIGHMEM:包含896MB以上的內(nèi)存頁(yè)框,不進(jìn)行直接映射,可以通過(guò)永久映射和臨時(shí)映射進(jìn)行這部分內(nèi)存頁(yè)框的訪問(wèn)。

整個(gè)結(jié)構(gòu)如下圖


poYBAGKDjsuAdyTYAABBgN-tEd0301.jpg?source=d16d100b


對(duì)于ZONE_DMA和ZONE_NORMAL這兩個(gè)管理區(qū),內(nèi)核地址都是進(jìn)行直接映射,只有ZONE_HIGHMEM管理區(qū)系統(tǒng)在默認(rèn)情況下是不進(jìn)行直接映射的,只有在需要使用的時(shí)候進(jìn)行映射(臨時(shí)映射或者永久映射)。

結(jié)點(diǎn)和管理區(qū)描述符

為了用于NUMA架構(gòu),使用了node用來(lái)描述一個(gè)地方的內(nèi)存。對(duì)于我們PC來(lái)說(shuō),一臺(tái)PC就是一個(gè)node。node用struct pglist_data結(jié)構(gòu)表示:

/* 內(nèi)存結(jié)點(diǎn)描述符,所有的結(jié)點(diǎn)描述符保存在 struct pglist_data *node_data[MAX_NUMNODES] 中 */
typedef struct pglist_data {
    /* 管理區(qū)描述符的數(shù)組 */
    struct zone node_zones[MAX_NR_ZONES]; 
    /* 頁(yè)分配器使用的zonelist數(shù)據(jù)結(jié)構(gòu)的數(shù)組,將所有結(jié)點(diǎn)的管理區(qū)按一定的關(guān)聯(lián)鏈接成一個(gè)鏈表,分配內(nèi)存時(shí)會(huì)按照此鏈表的順序進(jìn)行分配 */
    struct zonelist node_zonelists[MAX_ZONELISTS];
    /* 結(jié)點(diǎn)中管理區(qū)的個(gè)數(shù) */
    int nr_zones;
#ifdef CONFIG_FLAT_NODE_MEM_MAP    /* means !SPARSEMEM */
    /* 結(jié)點(diǎn)中頁(yè)描述符的數(shù)組,包含了此結(jié)點(diǎn)中所有頁(yè)框描述符,實(shí)際分配是是一個(gè)指針數(shù)組 */
    struct page *node_mem_map;
#ifdef CONFIG_MEMCG
    /* 用于資源限制機(jī)制 */
    struct page_cgroup *node_page_cgroup;
#endif
#endif
#ifndef CONFIG_NO_BOOTMEM
    /* 用在內(nèi)核初始化階段 */
    struct bootmem_data *bdata;
#endif
#ifdef CONFIG_MEMORY_HOTPLUG
    /* 自旋鎖 */
    spinlock_t node_size_lock;
#endif
    /* 結(jié)點(diǎn)中第一個(gè)頁(yè)框的下標(biāo),在numa系統(tǒng)中,頁(yè)框會(huì)有兩個(gè)序號(hào),所有頁(yè)框的一個(gè)序號(hào),還有就是在此結(jié)點(diǎn)中的一個(gè)序號(hào)
     * 比如結(jié)點(diǎn)2中的頁(yè)框1,它在結(jié)點(diǎn)2中的序號(hào)是1,但是在所有頁(yè)框中的序號(hào)是1001,這個(gè)變量就是保存這個(gè)結(jié)點(diǎn)首頁(yè)框的序號(hào)1000,用于方便轉(zhuǎn)換
     */
    unsigned long node_start_pfn;
    /* 內(nèi)存結(jié)點(diǎn)的大小,不包括洞(以頁(yè)框?yàn)閱挝? */
    unsigned long node_present_pages; 
    /* 結(jié)點(diǎn)的大小,包括洞(以頁(yè)框?yàn)閱挝? */
    unsigned long node_spanned_pages; 
    
    /* 結(jié)點(diǎn)標(biāo)識(shí)符 */
    int node_id;
    /* kswaped頁(yè)換出守護(hù)進(jìn)程使用的等待隊(duì)列 */
    wait_queue_head_t kswapd_wait;
    wait_queue_head_t pfmemalloc_wait;
    /* 指針指向kswapd內(nèi)核線程的進(jìn)程描述符 */
    struct task_struct *kswapd;    /* Protected by
                       mem_hotplug_begin/end() */
    /* kswapd將要?jiǎng)?chuàng)建的空閑塊大小取對(duì)數(shù)的值 */
    int kswapd_max_order;
    enum zone_type classzone_idx;
#ifdef CONFIG_NUMA_BALANCING
    /* 以下用于NUMA的負(fù)載均衡 */
    /* Lock serializing the migrate rate limiting window */
    spinlock_t numabalancing_migrate_lock;

    /* Rate limiting time interval */
    unsigned long numabalancing_migrate_next_window;

    /* Number of pages migrated during the rate limiting time interval */
    unsigned long numabalancing_migrate_nr_pages;
#endif
} pg_data_t;

系統(tǒng)中所有的結(jié)點(diǎn)描述符都保存在node_data這個(gè)數(shù)組中。在pg_data_t這個(gè)結(jié)點(diǎn)描述符中,node_zones數(shù)組中保存了這個(gè)結(jié)點(diǎn)中所有的管理區(qū)描述符,雖然系統(tǒng)將物理內(nèi)存分為三個(gè)區(qū),但是在邏輯上,系統(tǒng)分為了四個(gè)管理區(qū),多出的一個(gè)是ZONE_MOVABLE,這個(gè)區(qū)是一個(gè)虛擬的管理區(qū),它并沒有對(duì)應(yīng)于內(nèi)存的某個(gè)區(qū)域,它的主要目的就是為了避免內(nèi)存碎片化,它的內(nèi)存要么全部來(lái)自ZONE_HIGHMEM區(qū),要么全部來(lái)自ZONE_NORMAL區(qū)。這些我們?cè)诤竺娴某跏蓟瘮?shù)中將會(huì)看到。

每個(gè)結(jié)點(diǎn)都有一個(gè)內(nèi)核線程kswapd,它的作用就是將進(jìn)程或內(nèi)核持有的,但是不常用的頁(yè)交換到磁盤上,以騰出更多可用內(nèi)存。

我們?cè)倏纯垂芾韰^(qū)描述符:

/* 內(nèi)存管理區(qū)描述符 */
struct zone {
    /* Read-mostly fields */

    /* zone watermarks, access with *_wmark_pages(zone) macros */
    /* 包括pages_min,pages_low,pages_high
     * pages_min: 管理區(qū)中保留頁(yè)的數(shù)目
     * pages_low: 回收頁(yè)框使用的下界,同時(shí)也被管理區(qū)分配器作為閥值使用,一般這個(gè)數(shù)字是pages_min的5/4
     * pages_high: 回收頁(yè)框使用的上界,同時(shí)也被管理區(qū)分配器作為閥值使用,一般這個(gè)數(shù)字是pages_min的3/2
     */
    unsigned long watermark[NR_WMARK];

    /* 指明在處理內(nèi)存不足的臨界情況下管理區(qū)必須保留的頁(yè)框數(shù)目,同時(shí)也用于在中斷或臨界區(qū)發(fā)出的原子內(nèi)存分配請(qǐng)求(就是禁止阻塞的內(nèi)存分配請(qǐng)求) */
    long lowmem_reserve[MAX_NR_ZONES];

#ifdef CONFIG_NUMA
    int node;
#endif

    /*
     * The target ratio of ACTIVE_ANON to INACTIVE_ANON pages on
     * this zone's LRU.  Maintained by the pageout code.
     */
    unsigned int inactive_ratio;

    /* 指向此管理區(qū)屬于的結(jié)點(diǎn) */
    struct pglist_data    *zone_pgdat;
    /* 實(shí)現(xiàn)每CPU頁(yè)框高速緩存,里面包含每個(gè)CPU的單頁(yè)框的鏈表 */
    struct per_cpu_pageset __percpu *pageset;

    /*
     * This is a per-zone reserve of pages that should not be
     * considered dirtyable memory.
     */
    unsigned long        dirty_balance_reserve;

#ifndef CONFIG_SPARSEMEM
    /*
     * Flags for a pageblock_nr_pages block. See pageblock-flags.h.
     * In SPARSEMEM, this map is stored in struct mem_section
     */
    unsigned long        *pageblock_flags;
#endif /* CONFIG_SPARSEMEM */

#ifdef CONFIG_NUMA
    /*
     * zone reclaim becomes active if more unmapped pages exist.
     */
    unsigned long        min_unmapped_pages;
    unsigned long        min_slab_pages;
#endif /* CONFIG_NUMA */

    /* zone_start_pfn == zone_start_paddr >> PAGE_SHIFT */
    /* 管理區(qū)第一個(gè)頁(yè)框下標(biāo) */
    unsigned long        zone_start_pfn;

    /* 所有正常情況下可用的頁(yè),總頁(yè)數(shù)(不包括洞)減去保留的頁(yè)數(shù) */
    unsigned long        managed_pages;
    /* 管理區(qū)總大小(頁(yè)為單位),包括洞 */
    unsigned long        spanned_pages;
    /* 管理區(qū)總大小(頁(yè)為單位),不包括洞 */
    unsigned long        present_pages;
    /* 指向管理區(qū)的傳統(tǒng)名稱,"DMA" "NORMAL" "HighMem" */
    const char        *name;

    /* 對(duì)應(yīng)于伙伴系統(tǒng)中MIGRATE_RESEVE鏈的頁(yè)塊的數(shù)量 */
    int            nr_migrate_reserve_block;

#ifdef CONFIG_MEMORY_ISOLATION
    /*
     * Number of isolated pageblock. It is used to solve incorrect
     * freepage counting problem due to racy retrieving migratetype
     * of pageblock. Protected by zone->lock.
     */
    /* 在內(nèi)存隔離中表示隔離的頁(yè)框塊數(shù)量 */
    unsigned long        nr_isolate_pageblock;
#endif

#ifdef CONFIG_MEMORY_HOTPLUG
    /* see spanned/present_pages for more description */
    seqlock_t        span_seqlock;
#endif

    /* 進(jìn)程等待隊(duì)列的hash表,這些進(jìn)程在等待管理區(qū)中的某頁(yè) */
    wait_queue_head_t    *wait_table;
    /* 等待隊(duì)列散列表的大小 */
    unsigned long        wait_table_hash_nr_entries;
    /* 等待隊(duì)列散列表數(shù)組大小 */
    unsigned long        wait_table_bits;

    ZONE_PADDING(_pad1_)

    /* Write-intensive fields used from the page allocator */
    /* 保護(hù)該描述符的自旋鎖 */
    spinlock_t        lock;

    /* free areas of different sizes */
    /* 標(biāo)識(shí)出管理區(qū)中的空閑頁(yè)框塊,用于伙伴系統(tǒng) */
    /* MAX_ORDER為11,分別代表包含大小為1,2,4,8,16,32,64,128,256,512,1024個(gè)連續(xù)頁(yè)框的鏈表 */
    struct free_area    free_area[MAX_ORDER];

    /* zone flags, see below */
    /* 管理區(qū)標(biāo)識(shí) */
    unsigned long        flags;

    ZONE_PADDING(_pad2_)

    /* Fields commonly accessed by the page reclaim scanner */
    /* 活動(dòng)及非活動(dòng)鏈表使用的自旋鎖 */
    spinlock_t        lru_lock;
    struct lruvec        lruvec;

    /* Evictions & activations on the inactive file list */
    atomic_long_t        inactive_age;

    /*
     * When free pages are below this point, additional steps are taken
     * when reading the number of free pages to avoid per-cpu counter
     * drift allowing watermarks to be breached
     */
    unsigned long percpu_drift_mark;

#if defined CONFIG_COMPACTION || defined CONFIG_CMA
    /* pfn where compaction free scanner should start */
    unsigned long        compact_cached_free_pfn;
    /* pfn where async and sync compaction migration scanner should start */
    unsigned long        compact_cached_migrate_pfn[2];
#endif

#ifdef CONFIG_COMPACTION
    /*
     * On compaction failure, 1<

此管理區(qū)描述符中的實(shí)際把所有屬于該管理區(qū)的頁(yè)框保存在兩個(gè)地方:struct free_area free_area[MAX_ORDER]和struct per_cpu_pageset __percpu * pageset。free_area是這個(gè)管理區(qū)的伙伴系統(tǒng),而pageset是這個(gè)區(qū)的每CPU頁(yè)框高速緩存。對(duì)管理區(qū)的理解需要結(jié)合伙伴系統(tǒng)和每CPU頁(yè)框高速緩存

管理區(qū)頁(yè)框分配器(管理所有物理內(nèi)存頁(yè)框)

ZONE_NORMAL和ZONE_DMA的地址直接映射到了內(nèi)核地址空間,但是也不代表內(nèi)核的代碼可以隨心所欲的通過(guò)線性地址直接訪問(wèn)物理地址。內(nèi)核通過(guò)一個(gè)管理區(qū)頁(yè)框分配器管理著物理內(nèi)存上所有的頁(yè)框,在管理區(qū)分配器里的核心系統(tǒng)就是伙伴系統(tǒng)和每CPU頁(yè)框高速緩存(不是硬件上的高速緩存,只是名稱一樣)。在linux系統(tǒng)中,管理區(qū)頁(yè)框分配器管理著所有物理內(nèi)存,無(wú)論你是內(nèi)核還是進(jìn)程,需要將一些內(nèi)存占為己有時(shí),都需要請(qǐng)求管理區(qū)頁(yè)框分配器,這時(shí)才會(huì)分配給你應(yīng)該獲得的物理內(nèi)存頁(yè)框。當(dāng)你所擁有的頁(yè)框不再使用時(shí),你必須釋放這些頁(yè)框,讓這些頁(yè)框回到管理區(qū)頁(yè)框分配器當(dāng)中。特別的,對(duì)于高端內(nèi)存,即使從管理區(qū)頁(yè)框分配器中獲得了相應(yīng)的頁(yè)框,我們還需要進(jìn)行映射才能夠使用。

有時(shí)候目標(biāo)管理區(qū)不一定有足夠的頁(yè)框去滿足分配,這時(shí)候系統(tǒng)會(huì)從另外兩個(gè)管理區(qū)中獲取要求的頁(yè)框,但這是按照一定規(guī)則去執(zhí)行的,如下:

如果要求從DMA區(qū)中獲取,就只能從ZONE_DMA區(qū)中獲取。

如果沒有規(guī)定從哪個(gè)區(qū)獲取,就按照順序從 ZONE_NORMAL -> ZONE_DMA 獲取。

如果規(guī)定從HIGHMEM區(qū)獲取,就按照順序從 ZONE_HIGHMEM -> ZONE_NORMAL -> ZONE_DMA 獲取。

注意系統(tǒng)是不允許在一次分配中從不同的兩個(gè)管理區(qū)獲取頁(yè)框的,并且當(dāng)請(qǐng)求多個(gè)頁(yè)框時(shí),從伙伴系統(tǒng)中分配給目標(biāo)的頁(yè)框是連續(xù)的,并且請(qǐng)求的頁(yè)數(shù)必須是2的次方個(gè)數(shù)。


pYYBAGKDjsuALo0WAAB4-sWNiTY187.jpg?source=d16d100b

?

管理區(qū)分配器主要做的事情就是將頁(yè)框通過(guò)伙伴系統(tǒng)或者每CPU頁(yè)框高速緩存分配出去,這里涉及到三個(gè)結(jié)構(gòu),頁(yè)描述符,伙伴系統(tǒng),每CPU高速緩存。

我們先說(shuō)說(shuō)頁(yè)描述符,頁(yè)描述符實(shí)際上并不專屬于描述頁(yè)框,它還用于描述一個(gè)SLAB分配器和SLUB分配器,這個(gè)之后再說(shuō),我們先說(shuō)關(guān)于頁(yè)的:

/* 頁(yè)描述符,描述一個(gè)頁(yè)框,也會(huì)用于描述一個(gè)SLAB,相當(dāng)于同時(shí)是頁(yè)描述符,也是SLAB描述符 */
struct page {
    /* First double word block */
    /* 用于頁(yè)描述符,一組標(biāo)志(如PG_locked、PG_error),也對(duì)頁(yè)框所在的管理區(qū)和node進(jìn)行編號(hào) */
    unsigned long flags;        /* Atomic flags, some possibly
                     * updated asynchronously */
    union {
        /* 用于頁(yè)描述符,當(dāng)頁(yè)被插入頁(yè)高速緩存中時(shí)使用,或者當(dāng)頁(yè)屬于匿名區(qū)時(shí)使用 */
        struct address_space *mapping;  
        /* 用于SLAB描述符,用于執(zhí)行第一個(gè)對(duì)象的地址 */
        void *s_mem;            /* slab first object */
    };


    /* Second double word */
    struct {
        union {
            /* 作為不同的含義被幾種內(nèi)核成分使用。例如,它在頁(yè)磁盤映像或匿名區(qū)中標(biāo)識(shí)存放在頁(yè)框中的數(shù)據(jù)的位置,或者它存放一個(gè)換出頁(yè)標(biāo)識(shí)符 */
            pgoff_t index;        /* Our offset within mapping. */
            /* 用于SLAB描述符,指向第一個(gè)空閑對(duì)象地址 */
            void *freelist;    
            /* 當(dāng)管理區(qū)頁(yè)框分配器壓力過(guò)大時(shí),設(shè)置這個(gè)標(biāo)志就確保這個(gè)頁(yè)框?qū)iT用于系統(tǒng)釋放其他頁(yè)框時(shí)使用 */
            bool pfmemalloc;  
        };

        union {
#if defined(CONFIG_HAVE_CMPXCHG_DOUBLE) && defined(CONFIG_HAVE_ALIGNED_STRUCT_PAGE)
            /* SLUB使用 */
            unsigned long counters;
#else
            /* SLUB使用 */
            unsigned counters;
#endif

            struct {
                union {
                    /* 頁(yè)框中的頁(yè)表項(xiàng)計(jì)數(shù),如果沒有為-1,如果為PAGE_BUDDY_MAPCOUNT_VALUE(-128),說(shuō)明此頁(yè)及其后的一共2的private次方個(gè)數(shù)頁(yè)框處于伙伴系統(tǒng)中,正在使用時(shí)應(yīng)該是0 */
                    atomic_t _mapcount;

                    struct { /* SLUB使用 */
                        unsigned inuse:16;
                        unsigned objects:15;
                        unsigned frozen:1;
                    };
                    int units;    /* SLOB */
                };
                /* 頁(yè)框的引用計(jì)數(shù),如果為0,則此頁(yè)框空閑,并可分配給任一進(jìn)程或內(nèi)核;如果大于0,則說(shuō)明頁(yè)框被分配給了一個(gè)或多個(gè)進(jìn)程,或用于存放內(nèi)核數(shù)據(jù)。page_count()返回_count的值,也就是該頁(yè)的使用者數(shù)目 */
                atomic_t _count;        /* Usage count, see below. */
            };
            /* 用于SLAB描述符 */
            unsigned int active;    /* SLAB */
        };
    };


    /* Third double word block */
    union {
        /* 包含到頁(yè)的最近最少使用(LRU)雙向鏈表的指針,用于插入伙伴系統(tǒng)的空閑鏈表中,只有塊中頭頁(yè)框要被插入 */
        struct list_head lru;    


        /* SLAB使用 */
        struct {        /* slub per cpu partial pages */
            struct page *next;    /* Next partial slab */
#ifdef CONFIG_64BIT
            int pages;    /* Nr of partial slabs left */
            int pobjects;    /* Approximate # of objects */
#else
            short int pages;
            short int pobjects;
#endif
        };

        struct slab *slab_page; /* slab fields */
        struct rcu_head rcu_head;


#if defined(CONFIG_TRANSPARENT_HUGEPAGE) && USE_SPLIT_PMD_PTLOCKS
        pgtable_t pmd_huge_pte; /* protected by page->ptl */
#endif
    };


    /* Remainder is not double word aligned */
    union {
        /* 可用于正在使用頁(yè)的內(nèi)核成分(例如: 在緩沖頁(yè)的情況下它是一個(gè)緩沖器頭指針,如果頁(yè)是空閑的,則該字段由伙伴系統(tǒng)使用,在給伙伴系統(tǒng)使用時(shí),表明的是塊的2的次方數(shù),只有塊的第一個(gè)頁(yè)框會(huì)使用) */
        unsigned long private;        
#if USE_SPLIT_PTE_PTLOCKS
#if ALLOC_SPLIT_PTLOCKS
        spinlock_t *ptl;
#else
        spinlock_t ptl;
#endif
#endif
        /* SLAB描述符使用,指向SLAB的高速緩存 */
        struct kmem_cache *slab_cache;    /* SL[AU]B: Pointer to slab */
        struct page *first_page;    /* Compound tail pages */
    };

#if defined(WANT_PAGE_VIRTUAL)
    /* 此頁(yè)框第一個(gè)物理地址對(duì)應(yīng)的線性地址,如果是沒有映射的高端內(nèi)存的頁(yè)框,則為空 */
    void *virtual; 
#endif /* WANT_PAGE_VIRTUAL */
#ifdef CONFIG_WANT_PAGE_DEBUG_FLAGS
    unsigned long debug_flags;    /* Use atomic bitops on this */
#endif

#ifdef CONFIG_KMEMCHECK
    void *shadow;
#endif

#ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS
    int _last_cpupid;
#endif
}
 

在struct page描述一個(gè)頁(yè)框時(shí),我們比較關(guān)注的成員變量有unsigned long flags、struct list_head lru和atomic_t _count。

flags:包含有很多信息,包括此頁(yè)框?qū)儆诘膎ode結(jié)點(diǎn)號(hào),此頁(yè)框?qū)儆诘膠one號(hào)和此頁(yè)框的屬性。

lru:用于將此頁(yè)描述符放入相應(yīng)的鏈表,比如伙伴系統(tǒng)或者每CPU頁(yè)框高速緩存。

_count:代表頁(yè)框的引用計(jì)數(shù),0代表此頁(yè)框空閑,大于0代表此頁(yè)框分配給了多少個(gè)進(jìn)程使用(共享)。

linux為了防止內(nèi)存中產(chǎn)生過(guò)多的碎片,一般把頁(yè)的類型分為三種:

不可移動(dòng)頁(yè):在內(nèi)存中有固定位置,不能移動(dòng)到其他地方。內(nèi)核中使用的頁(yè)大部分是屬于這種類型。

可回收頁(yè):不能直接移動(dòng),但可以刪除,頁(yè)中的內(nèi)容可以從某些源中重新生成。例如,頁(yè)內(nèi)容是映射到文件數(shù)據(jù)的頁(yè)就屬于這種類型。對(duì)于這種類型,在內(nèi)存短缺(分配失敗)時(shí),會(huì)發(fā)起內(nèi)存回收,將這類型頁(yè)進(jìn)行回寫釋放。

可移動(dòng)頁(yè):可隨意移動(dòng),用戶空間的進(jìn)程使用的沒有映射具體磁盤文件的頁(yè)就屬于這種類型(比如堆、棧、shmem共享內(nèi)存、匿名mmap共享內(nèi)存),它們是通過(guò)進(jìn)程頁(yè)表映射的,把這些頁(yè)復(fù)制到新位置時(shí),只要更新進(jìn)程頁(yè)表就可以了。一般這些頁(yè)是從高端內(nèi)存管理區(qū)獲取。

伙伴系統(tǒng)

伙伴系統(tǒng)的主要作用就是減少物理內(nèi)存的外部碎片(SLAB/SLUB減少頁(yè)框的內(nèi)部碎片),它實(shí)際上是一個(gè)struct free_area的數(shù)組,數(shù)組長(zhǎng)度是MAX_ORDER,也就是11,代表著每個(gè)數(shù)組元素中鏈表上保存的連續(xù)頁(yè)框長(zhǎng)度是2的order次方。free_area[0]中鏈表保存的是長(zhǎng)度為1的頁(yè)框,free_area[1]中鏈表上保存的是物理上連續(xù)的兩個(gè)頁(yè)框的首頁(yè)框鏈表,free_area[2]中鏈表上保存的是物理上連續(xù)4個(gè)頁(yè)框的首頁(yè)框鏈表,free_area[10]中鏈表上保存的是物理上連續(xù)1024個(gè)頁(yè)框的首頁(yè)框鏈表,所以整個(gè)伙伴系統(tǒng)中將管理區(qū)中的頁(yè)框分為連續(xù)的1,2,4,8,16,32,64,128,256,512,1024頁(yè)框放入不同鏈表中保存起來(lái)。而因?yàn)榛锇橄到y(tǒng)中每個(gè)鏈表保存的頁(yè)框都是連續(xù)的,所以只有第一個(gè)頁(yè)框會(huì)加入到鏈表中,因?yàn)橛衞rder,也可以知道此頁(yè)框之后的多少個(gè)頁(yè)框是屬于這一小塊連續(xù)頁(yè)框的。當(dāng)需要在普通內(nèi)存區(qū)申請(qǐng)4個(gè)頁(yè)框大小的內(nèi)存時(shí),系統(tǒng)會(huì)到普通內(nèi)存管理區(qū)的伙伴系統(tǒng)中的free_area[2]中的第一個(gè)鏈表結(jié)點(diǎn),這個(gè)結(jié)點(diǎn)的頁(yè)框及其之后3個(gè)頁(yè)框都是空閑的,然后把首頁(yè)框返回給申請(qǐng)者。

/* 伙伴系統(tǒng)的一個(gè)塊,描述1,2,4,8,16,32,64,128,256,512或1024個(gè)連續(xù)頁(yè)框的塊 */
struct free_area {
    /* 指向這個(gè)塊中所有空閑小塊的第一個(gè)頁(yè)描述符,這些小塊會(huì)按照MIGRATE_TYPES類型存放在不同指針里 */
    struct list_head    free_list[MIGRATE_TYPES];
    /* 空閑小塊的個(gè)數(shù) */
    unsigned long        nr_free;
};
 

在伙伴系統(tǒng)中,因?yàn)轫?yè)的分類關(guān)系,在每種長(zhǎng)度相同的連續(xù)頁(yè)框中又會(huì)分出多個(gè)不同類型的鏈表,如下,

enum {
    MIGRATE_UNMOVABLE,         /* 不可移動(dòng)頁(yè) */
    MIGRATE_RECLAIMABLE,       /* 可回收頁(yè) */
    MIGRATE_MOVABLE,           /* 可移動(dòng)頁(yè) */
    MIGRATE_PCPTYPES,          /* 用來(lái)表示每CPU頁(yè)框高速緩存的數(shù)據(jù)結(jié)構(gòu)中的鏈表的可移動(dòng)類型數(shù)目 */
    MIGRATE_RESERVE = MIGRATE_PCPTYPES,     
#ifdef CONFIG_CMA
    MIGRATE_CMA,
#endif
#ifdef CONFIG_MEMORY_ISOLATION
    MIGRATE_ISOLATE,          /* 不能從這個(gè)鏈表分配頁(yè)框,因?yàn)檫@個(gè)鏈表專門用于NUMA結(jié)點(diǎn)移動(dòng)物理內(nèi)存頁(yè),將物理內(nèi)存頁(yè)移動(dòng)到使用這個(gè)頁(yè)最頻繁的CPU */
#endif
    MIGRATE_TYPES
};

保存連續(xù)2個(gè)頁(yè)框的free_area[2]的結(jié)構(gòu)如下:


poYBAGKDjsuAXcjKAABWBfTeIxc982.jpg?source=d16d100b

?

在從伙伴系統(tǒng)中申請(qǐng)頁(yè)框時(shí),有可能會(huì)遇到一種情況,就是當(dāng)前需求的連續(xù)頁(yè)框鏈表上沒有可用的空閑頁(yè)框,這時(shí)后,伙伴系統(tǒng)會(huì)從下一級(jí)獲取一個(gè)連續(xù)長(zhǎng)度的頁(yè)框塊,將其拆分放入這級(jí)列表;當(dāng)然在擁有者釋放連續(xù)頁(yè)框時(shí)伙伴系統(tǒng)也會(huì)適當(dāng)?shù)剡M(jìn)行連續(xù)頁(yè)框的合并,并放入下一級(jí)中。比如:我需要申請(qǐng)4個(gè)頁(yè)框,但是長(zhǎng)度為4個(gè)連續(xù)頁(yè)框塊鏈表沒有空閑的頁(yè)框塊,伙伴系統(tǒng)會(huì)從連續(xù)8個(gè)頁(yè)框塊的鏈表獲取一個(gè),并將其拆分為兩個(gè)連續(xù)4個(gè)頁(yè)框塊,放入連續(xù)4個(gè)頁(yè)框塊的鏈表中。釋放時(shí)道理也一樣,會(huì)檢查釋放的這幾個(gè)頁(yè)框的之前和之后的物理頁(yè)框是否空閑,并且能否組成下一級(jí)長(zhǎng)度的塊。

每CPU頁(yè)框高速緩存

每CPU頁(yè)框高速緩存也是一個(gè)分配器,配合著伙伴系統(tǒng)進(jìn)行使用,這個(gè)分配器是專門用于分配單個(gè)頁(yè)框的,它維護(hù)一個(gè)單頁(yè)框的雙向鏈表,為什么需要這個(gè)分配器,原因主要有兩點(diǎn):

因?yàn)槊總€(gè)CPU都有自己的硬件高速緩存,當(dāng)對(duì)一個(gè)頁(yè)進(jìn)行讀取寫入時(shí),首先會(huì)把這個(gè)頁(yè)裝入硬件高速緩存,而如果進(jìn)程對(duì)這個(gè)處于硬件高速緩存的頁(yè)進(jìn)行操作后立即釋放掉,這個(gè)頁(yè)有可能還保存在硬件高速緩存中,這樣我另一個(gè)進(jìn)程需要請(qǐng)求一個(gè)頁(yè)并立即寫入數(shù)據(jù)的話,分配器將這個(gè)處于硬件高速緩存中的頁(yè)分配給它,系統(tǒng)效率會(huì)大大增加。

減少鎖的競(jìng)爭(zhēng),假設(shè)單頁(yè)框都是使用free_area來(lái)管理,那么多個(gè)CPU同時(shí)頻繁訪問(wèn)時(shí),每次都是只能單CPU獲取到頁(yè)框,其他CPU等待,這會(huì)造成大量的鎖競(jìng)爭(zhēng),導(dǎo)致分配效率降低。

在每CPU頁(yè)框高速緩存中用一個(gè)鏈表來(lái)維護(hù)一個(gè)單頁(yè)框的雙向鏈表,每個(gè)CPU都有自己的鏈表(因?yàn)槊總€(gè)CPU有自己的硬件高速緩存),那些比較可能處于硬件高速緩存中的頁(yè)被稱為“熱頁(yè)”,比較不可能處于硬件高速緩存中的頁(yè)稱為“冷頁(yè)”。其實(shí)系統(tǒng)判斷是否為熱頁(yè)還是冷頁(yè)很簡(jiǎn)單,越最近釋放的頁(yè)就比較可能是熱頁(yè),所以在雙向鏈表中,從鏈表頭插入可能是熱頁(yè)的單頁(yè)框,在鏈表尾插入可能是冷頁(yè)的單頁(yè)框。分配時(shí)熱頁(yè)就從鏈表頭獲取,冷頁(yè)就從鏈表尾獲取。

在每CPU頁(yè)框高速緩存中也可能會(huì)遇到?jīng)]有空閑的頁(yè)框(被分配完了),這時(shí)候每CPU頁(yè)框高速緩存會(huì)從伙伴系統(tǒng)中拿出頁(yè)框放入每CPU頁(yè)框高速緩存中,相反,如果每CPU頁(yè)框高速緩存中頁(yè)框過(guò)多,也會(huì)將一些頁(yè)框放回伙伴系統(tǒng)。

在內(nèi)核中使用struct per_cpu_pageset結(jié)構(gòu)描述一個(gè)每CPU頁(yè)框高速緩存,其中的struct per_cpu_pages是核心結(jié)構(gòu)體,如下:

/* 描述一個(gè)CPU頁(yè)框高速緩存 */
struct per_cpu_pageset {
    /* 高速緩存頁(yè)框結(jié)構(gòu) */
    struct per_cpu_pages pcp;
#ifdef CONFIG_NUMA
    s8 expire;
#endif
#ifdef CONFIG_SMP
    s8 stat_threshold;
    s8 vm_stat_diff[NR_VM_ZONE_STAT_ITEMS];
#endif
};

struct per_cpu_pages {
    /* 當(dāng)前CPU高速緩存中頁(yè)框個(gè)數(shù) */
    int count;        /* number of pages in the list */
    /* 上界,當(dāng)此CPU高速緩存中頁(yè)框個(gè)數(shù)大于high,則會(huì)將batch個(gè)頁(yè)框放回伙伴系統(tǒng) */
    int high;        /* high watermark, emptying needed */
    /* 在高速緩存中將要添加或被刪去的頁(yè)框個(gè)數(shù) */
    int batch;        /* chunk size for buddy add/remove */

    /* Lists of pages, one per migrate type stored on the pcp-lists */
    /* 頁(yè)框的鏈表,如果需要冷高速緩存,從鏈表尾開始獲取頁(yè)框,如果需要熱高速緩存,從鏈表頭開始獲取頁(yè)框 */
    struct list_head lists[MIGRATE_PCPTYPES];
};

關(guān)于頁(yè)框回收

內(nèi)存中并非所有物理頁(yè)面都是可以進(jìn)行回收的,內(nèi)核占用的頁(yè)不會(huì)被換出,只有與用戶空間建立了映射關(guān)系的物理頁(yè)面才會(huì)被換出??偟膩?lái)說(shuō),以下這些種物理頁(yè)面可以被 Linux 操作系統(tǒng)回收:

進(jìn)程映射所占的頁(yè)面,包括代碼段,數(shù)據(jù)段,堆棧以及動(dòng)態(tài)分配的“存儲(chǔ)堆”(malloc分配的)。

用戶空間中通過(guò)mmap()把文件內(nèi)容映射到內(nèi)存所占的頁(yè)面。

匿名頁(yè)面(沒有映射到文件的都是匿名映射,用戶空間的堆和棧):進(jìn)程用戶模式下的堆棧以及是使用 mmap 匿名映射的內(nèi)存區(qū)(共享內(nèi)存區(qū))。注:堆棧所占頁(yè)面一般不被換出。

特殊的用于 slab 分配器的緩存,比如用于緩存文件目錄結(jié)構(gòu) dentry 的 cache,以及用于緩存索引節(jié)點(diǎn) inode 的 cache

tmpfs文件系統(tǒng)使用的頁(yè)。

Linux 操作系統(tǒng)使用如下這兩種機(jī)制檢查系統(tǒng)內(nèi)存的使用情況,從而確定可用的內(nèi)存是否太少?gòu)亩枰M(jìn)行頁(yè)面回收。

周期性的檢查:這是由后臺(tái)運(yùn)行的守護(hù)進(jìn)程 kswapd 完成的。該進(jìn)程定期檢查當(dāng)前系統(tǒng)的內(nèi)存使用情況,當(dāng)發(fā)現(xiàn)系統(tǒng)內(nèi)空閑的物理頁(yè)面數(shù)目少于特定的閾值時(shí),該進(jìn)程就會(huì)發(fā)起頁(yè)面回收的操作。

“內(nèi)存嚴(yán)重不足”事件的觸發(fā):在某些情況下,比如,操作系統(tǒng)忽然需要通過(guò)伙伴系統(tǒng)為用戶進(jìn)程分配一大塊內(nèi)存,或者需要?jiǎng)?chuàng)建一個(gè)很大的緩沖區(qū),而當(dāng)時(shí)系統(tǒng)中 的內(nèi)存沒有辦法提供足夠多的物理內(nèi)存以滿足這種內(nèi)存請(qǐng)求,這時(shí)候,操作系統(tǒng)就必須盡快進(jìn)行頁(yè)面回收操作,以便釋放出一些內(nèi)存空間從而滿足上述的內(nèi)存請(qǐng)求。 這種頁(yè)面回收方式也被稱作“直接頁(yè)面回收”。

如果操作系統(tǒng)在進(jìn)行了內(nèi)存回收操作之后仍然無(wú)法回收到足夠多的頁(yè)面以滿足上述內(nèi)存要求,那么操作系統(tǒng)只有最后一個(gè)選擇,那就是使用 OOM( out of memory )killer,它從系統(tǒng)中挑選一個(gè)最合適的進(jìn)程殺死它,并釋放該進(jìn)程所占用的所有頁(yè)面。

結(jié)尾

下篇再說(shuō)slab了,內(nèi)容太多。到這里,記住對(duì)于物理內(nèi)存來(lái)說(shuō),系統(tǒng)都是以頁(yè)框作為最小的分配單位,而分配時(shí)必定是要通過(guò)管理區(qū)分配器進(jìn)行分配的,在管理區(qū)分配器中又必定是通過(guò)伙伴系統(tǒng)或每CPU頁(yè)框分配器進(jìn)行分配的,而我們編程使用到的malloc或者內(nèi)核中使用的分配小額內(nèi)存的情況,是使用slab實(shí)現(xiàn)的,slab的作用就是將一個(gè)頁(yè)框細(xì)分為多個(gè)小塊內(nèi)存。

審核編輯:湯梓紅
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11314

    瀏覽量

    209786
  • 模型
    +關(guān)注

    關(guān)注

    1

    文章

    3255

    瀏覽量

    48902
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    詳解linux設(shè)備驅(qū)動(dòng)模型架構(gòu)

    LDD3中說(shuō):“Linux內(nèi)核需要個(gè)對(duì)系統(tǒng)結(jié)構(gòu)的般性描述。”這個(gè)描述就是linux設(shè)備驅(qū)動(dòng)模型(下面簡(jiǎn)稱為L(zhǎng)DDM)。LDDM不是獨(dú)立
    發(fā)表于 07-25 07:25

    linux下的IO模型詳解

      開門見山,Linux下的如中IO模型:阻塞IO模型,非阻塞IO模型,IO復(fù)用模型,信號(hào)驅(qū)動(dòng)IO模型
    發(fā)表于 10-09 16:12

    詳解藍(lán)牙模塊原理與結(jié)構(gòu)

    電子發(fā)燒友網(wǎng)站提供《詳解藍(lán)牙模塊原理與結(jié)構(gòu).pdf》資料免費(fèi)下載
    發(fā)表于 11-26 16:40 ?94次下載

    詳解Linux內(nèi)存子系統(tǒng)

    MMU 是種硬件電路,它包含兩個(gè)部件,個(gè)是分段部件,個(gè)是分頁(yè)部件
    的頭像 發(fā)表于 04-20 08:20 ?1404次閱讀

    ElasticSearch深度分頁(yè)詳解

    ElasticSearch 是個(gè)實(shí)時(shí)的分布式搜索與分析引擎,常用于大量非結(jié)構(gòu)化數(shù)據(jù)的存儲(chǔ)和快速檢索場(chǎng)景,具有很強(qiáng)的擴(kuò)展性。縱使其有諸多優(yōu)點(diǎn),在搜索領(lǐng)域遠(yuǎn)超關(guān)系型數(shù)據(jù)庫(kù),但依然存在與關(guān)系型數(shù)據(jù)庫(kù)同樣的深度分頁(yè)問(wèn)題,本文就此問(wèn)題做
    的頭像 發(fā)表于 11-17 09:53 ?921次閱讀

    詳解精密封裝技術(shù)

    詳解精密封裝技術(shù)
    的頭像 發(fā)表于 12-30 15:41 ?1677次閱讀

    詳解分立元件門電路

    詳解分立元件門電路
    的頭像 發(fā)表于 03-27 17:44 ?3250次閱讀
    <b class='flag-5'>一</b><b class='flag-5'>文</b><b class='flag-5'>詳解</b>分立元件門電路

    圖文詳解Linux分頁(yè)機(jī)制

    分頁(yè)機(jī)制是 80x86 內(nèi)存管理機(jī)制的第二種機(jī)制,分段機(jī)制用于把虛擬地址轉(zhuǎn)換為線性地址,而分頁(yè)機(jī)制用于把線性地址轉(zhuǎn)換為物理地址。
    發(fā)表于 05-30 09:10 ?474次閱讀
    圖文<b class='flag-5'>詳解</b><b class='flag-5'>Linux</b><b class='flag-5'>分頁(yè)</b>機(jī)制

    詳解pcb和smt的區(qū)別

    詳解pcb和smt的區(qū)別
    的頭像 發(fā)表于 10-08 09:31 ?3393次閱讀

    詳解pcb地孔的作用

    詳解pcb地孔的作用
    的頭像 發(fā)表于 10-30 16:02 ?1687次閱讀

    詳解pcb不良分析

    詳解pcb不良分析
    的頭像 發(fā)表于 11-29 17:12 ?1196次閱讀

    mybatis邏輯分頁(yè)和物理分頁(yè)的區(qū)別

    MyBatis是個(gè)開源的Java持久層框架,它與其他ORM(對(duì)象關(guān)系映射)框架相比,具有更加靈活和高性能的特點(diǎn)。MyBatis提供了兩種分頁(yè)方式,即邏輯分頁(yè)和物理分頁(yè)。在本文中,我們
    的頭像 發(fā)表于 12-03 14:54 ?933次閱讀

    詳解pcb的msl等級(jí)

    詳解pcb的msl等級(jí)
    的頭像 發(fā)表于 12-13 16:52 ?9824次閱讀

    詳解pcb微帶線設(shè)計(jì)

    詳解pcb微帶線設(shè)計(jì)
    的頭像 發(fā)表于 12-14 10:38 ?3390次閱讀

    詳解pcb的組成和作用

    詳解pcb的組成和作用
    的頭像 發(fā)表于 12-18 10:48 ?1589次閱讀
    主站蜘蛛池模板: 99精品电影| 文中字幕一区二区三区视频播放| 久久永久影院免费| 小玲被公扒开腿| www伊人网| 麻生希快播在线| 中文字幕 日韩 无码 在线| 好吊日视频在线| 卫生间被教官做好爽HH视频| 俄罗斯美女z0z0z0在线| 全球真实小U女视频合集| 99热久久视频只有精品6国产| 久久这里只精品热在线18| 永久免费在线视频| 久久中文字幕免费高清| 尤物99久久久合集一区区| 久久精品热只有精品| 永久免费看bbb| 美女尿口羞羞视频| 97超级碰久久久久香蕉人人| 蜜臀AV中文字幕熟女人妻| 99久久免费精品国产| 欧美黑人巨大性极品hd欧| jyzzjyzzz视频国产在线观看| 欧美最猛性xxxxx亚洲精品| 草莓AV福利网站导航| 爽爽窝窝午夜精品一区二区| 国产 精品 亚洲 欧美 高清| 乌克兰成人性色生活片| 国产亚洲精品久久无亚洲| 亚洲字幕久久| 奶水太多h室友| 大学生一级毛片免费看| 晓雪老师我要进你里面好爽| 娇妻归来在线观看免费完整版电影 | 俄罗斯乌克兰战争原因 | 日韩欧美一级| 国产人妻XXXX精品HD电影| 亚洲色播永久网址大全| 免费网站在线观看国产v片| 俄罗斯粗大猛烈18P|