日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

linux 初始化内存管理_Linux内存管理第二章 -- Describing Physical Memory

發(fā)布時(shí)間:2025/3/20 linux 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux 初始化内存管理_Linux内存管理第二章 -- Describing Physical Memory 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

首先來(lái)描述幾個(gè)名詞:

  • NUMA:Non-Uniform Memory Access即內(nèi)存非一致性訪問(wèn).在很多巨星機(jī)上內(nèi)存被分成了多個(gè)區(qū)域,CPU訪問(wèn)不同的內(nèi)存區(qū)域耗費(fèi)的時(shí)間是不同的.但在同一區(qū)域內(nèi),CPU訪問(wèn)這些內(nèi)存的時(shí)間是一致的。
  • UMA:Uniform Memory Access即內(nèi)存一致性訪問(wèn),即所CPU訪問(wèn)所有內(nèi)存的耗時(shí)都是一致的。
  • 3. node:NUMA架構(gòu)下的每一個(gè)內(nèi)存區(qū)域稱作是node,node用struct pglist_data來(lái)描述。所有的node在Linux內(nèi)核中用一個(gè)全局鏈表pgdat_list關(guān)聯(lián)起來(lái),pg_data_t->pgdat_next指向下一個(gè)區(qū)域。

    4. zone:每一個(gè)node下面的內(nèi)存又被劃分為多個(gè)小區(qū)域,每個(gè)區(qū)域稱作是zone.zone一般分為三個(gè):ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM.Linux中使用struct zone來(lái)描述一個(gè)zone。

    5. page:Linux內(nèi)核對(duì)每一個(gè)物理內(nèi)存頁(yè)框的描述。

    node和zone的關(guān)系如下圖:

    Nodes

    Linux內(nèi)核中每一個(gè)node由struct pglist_data來(lái)進(jìn)行描述。其定義如下<include/linux/mmzone.h>,下面來(lái)介紹下主要成員的作用。

    typedef struct pglist_data {struct zone node_zones[MAX_NR_ZONES];struct zonelist node_zonelists[GFP_ZONETYPES];int nr_zones;struct page *node_mem_map;struct bootmem_data *bdata;unsigned long node_start_pfn;unsigned long node_present_pages; /* total number of physical pages */unsigned long node_spanned_pages; /* total size of physical page range, including holes */int node_id;struct pglist_data *pgdat_next;wait_queue_head_t kswapd_wait;struct task_struct *kswapd; } pg_data_t;

    node_zones:當(dāng)前node中的zones:ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM

    node_zonelists:該數(shù)組決定了當(dāng)前node中分配物理內(nèi)存時(shí),選擇zone的順序.該數(shù)組的初始化由mm/page_alloc.c中的build_zonelists()來(lái)完成。build_zonelists()由free_area_init_core()調(diào)用觸發(fā)。一般狀況下當(dāng)從ZONE_HIGHMEM中分配內(nèi)存失敗時(shí),會(huì)再嘗試從ZONE_NORMAL或者ZONE_DMA中分配。

    node_mem_map:該指針表示當(dāng)前node中的物理頁(yè)框數(shù)字中的第一個(gè)頁(yè)框描述符struct page的地址。在內(nèi)核中有些地方也可以直接使用全局?jǐn)?shù)組mem_map來(lái)訪問(wèn)獲取node的第一個(gè)page地址,實(shí)現(xiàn)node_mem_map相同的功能。

    node_start_paddr:當(dāng)前node的物理地址的起始地址。該字段類型是一個(gè)unsigned long型,有的時(shí)候可能會(huì)超長(zhǎng),一個(gè)比較好的頒發(fā)是在此字段中中記錄物理頁(yè)框號(hào)(Page Frame Number,PFN)。PFN = page_phys_addr >> PAGE_SHIFT

    node_start_mapnr:當(dāng)前node中起始物理地址在全局?jǐn)?shù)組mem_map中的偏移。

    node_size:當(dāng)前node中page的總數(shù)。

    系統(tǒng)中維護(hù)的所有node都在一個(gè)全局列表pgdat_list中。該list由init_bootmem_core()函數(shù)初始化。

    Zones

    Linux內(nèi)核中每一個(gè)zone由struct zone結(jié)構(gòu)來(lái)描述。該結(jié)構(gòu)主要使用來(lái)統(tǒng)計(jì)page的使用和釋放信息。其定義如下,接下來(lái)再看下重點(diǎn)成員:

    struct zone {spinlock_t lock;unsigned long free_pages;unsigned long pages_min, pages_low, pages_high;unsigned long protection[MAX_NR_ZONES];ZONE_PADDING(_pad1_)spinlock_t lru_lock; struct list_head active_list;struct list_head inactive_list;unsigned long nr_scan_active;unsigned long nr_scan_inactive;unsigned long nr_active;unsigned long nr_inactive;int all_unreclaimable; /* All pages pinned */unsigned long pages_scanned; /* since last reclaim */ZONE_PADDING(_pad2_)int temp_priority;int prev_priority;struct free_area free_area[MAX_ORDER];wait_queue_head_t * wait_table;unsigned long wait_table_size;unsigned long wait_table_bits;ZONE_PADDING(_pad3_)struct per_cpu_pageset pageset[NR_CPUS];struct pglist_data *zone_pgdat;struct page *zone_mem_map;/* zone_start_pfn == zone_start_paddr >> PAGE_SHIFT */unsigned long zone_start_pfn;char *name;unsigned long spanned_pages; /* total size, including holes */unsigned long present_pages; /* amount of memory (excluding holes) */ };

    - free_pages:當(dāng)前zone中free page的總個(gè)數(shù)

    - pages_min,pages_low,page_high:該zone中的水位線,具體在下一小節(jié)進(jìn)行說(shuō)明。

    - free_area:buddy allocator使用的free area bitmaps

    - wait_table:等待某個(gè)page被釋放的進(jìn)程的等待隊(duì)列鏈表。這個(gè)隊(duì)列對(duì)于wait_on_page()和unlock_page()函數(shù)來(lái)說(shuō)非常重要。

    - wait_table_size:wait_table中等待隊(duì)列的個(gè)數(shù)。

    - wait_table_bits:wait_table_size == (1 << wait_table_bits)

    - zone_pgdat:指向當(dāng)前zone所屬的node

    - zone_mem_map:當(dāng)前zone中的第一個(gè)page的地址,該page輸入全局?jǐn)?shù)組mem_map

    - size:當(dāng)前zone中pages 的個(gè)數(shù)

    Zone初始化

    當(dāng)內(nèi)核的頁(yè)表有page_init()建立好之后,就開會(huì)初始化zone。每種硬件架構(gòu)執(zhí)行zone初始化的時(shí)候可能稍有不同,但是最終目標(biāo)是相同的。每種架構(gòu)初始化zone的流程中的最終目標(biāo)是決定傳入什么參數(shù)值到UMA架構(gòu)的初始化函數(shù)free_area_init()或者NUMA架構(gòu)的初始化函數(shù)free_area_init_node()中。下面來(lái)看下參數(shù)的含義:

    void __init free_area_init_node(int nid, struct pglist_data *pgdat,unsigned long *zones_size, unsigned long node_start_pfn,unsigned long *zholes_size) {............ }

    - nid:要被初始化的zone所屬的node的邏輯標(biāo)識(shí)符。

    - pgdat:要被初始化成zone的node

    - zones_size:包含每個(gè)zone的page的個(gè)數(shù)的數(shù)組

    - node_start_pfn:當(dāng)前node的起始PFN

    - zholes_size:包含每個(gè)zone中memory holes的個(gè)數(shù)的數(shù)組。

    無(wú)論是free_area_init()還是free_area_init_node()都會(huì)調(diào)用free_area_init_core()來(lái)真正初始化每個(gè)struct zone中的成員。在做zone初始化的時(shí)候,系統(tǒng)無(wú)法知道每個(gè)zone中有多少可用的page,這些信息知道boot memory allocator退休之后才會(huì)知道。

    mem_map初始化

    mem_map是在系統(tǒng)啟動(dòng)階段被創(chuàng)建。在NUMA系統(tǒng)中,全局?jǐn)?shù)組mem_map被當(dāng)做是一個(gè)虛擬的數(shù)組其起始位置為PAGE_OFFSET(一般就是指虛擬地址3G的位置)。系統(tǒng)中的每個(gè)node初始化的過(guò)程中調(diào)用free_area_init_node()來(lái)為這個(gè)虛擬數(shù)組分配填充mem_map的一部分內(nèi)容。而在UMA系統(tǒng)中,因?yàn)橹挥幸粋€(gè)node,所以其node為全局變量contig_page_data,而全局變量mem_map就是這個(gè)contig_page_data中的ode_mem_map。

    對(duì)于UMA架構(gòu)來(lái)說(shuō),核心函數(shù)free_area_core()將為當(dāng)前初始化的node分配一個(gè)本地的lmem_map,lmem_map是從boot memory allocator中使用alloc_bootmem_node()來(lái)進(jìn)行分配。該新分配的lmem_map將會(huì)成為全局的mem_map。但NUMA系統(tǒng)中與此有稍微不同。

    對(duì)于NUMA架構(gòu)來(lái)說(shuō),核心函數(shù)free_area_core()將從node節(jié)點(diǎn)自己的內(nèi)存中分配lmem_map,而全部變量mem_map不會(huì)顯式的分配,而是將其設(shè)定為PAGE_OFFSET的虛擬地址上,其被當(dāng)做是一個(gè)虛擬的數(shù)組。而lmem_map被存儲(chǔ)在node中的node_mem_map,pg_data_t->node_mem_map,其在虛擬數(shù)組mem_map的某個(gè)位置上。當(dāng)node和zone被初始化完整之后,mem_map將會(huì)被當(dāng)成一個(gè)真正的數(shù)組了。

    Pages

    page定義

    系統(tǒng)中的每一個(gè)物理頁(yè)框都有一個(gè)相應(yīng)地struct page,其用來(lái)持續(xù)跟蹤物理頁(yè)框的狀態(tài)。其定義如下:

    struct page {page_flags_t flags; /* Atomic flags, some possibly updated asynchronously */atomic_t _count; /* Usage count, see below. */atomic_t _mapcount; /* Count of ptes mapped in mms,to show when page is mapped & limit reverse map searches. unsigned long private; /* Mapping-private opaque data:usually used for buffer_heads if PagePrivate set;/* used for swp_entry_t if PageSwapCache */struct address_space *mapping; /* If low bit clear, points to inode address_space, or NULL.* If page mapped as anonymous memory, low bit is set, and it points to anon_vma object:* see PAGE_MAPPING_ANON below.*/pgoff_t index; /* Our offset within mapping. */struct list_head lru;/* Pageout list, eg. active_list protected by zone->lru_lock !*//* On machines where all RAM is mapped into kernel address space,* we can simply calculate the virtual address. On machines with* highmem some memory is mapped into kernel virtual memory* dynamically, so we need a place to store that address.* Note that this field could be 16 bits on x86 ... ;)** Architectures with slow multiplication can define* WANT_PAGE_VIRTUAL in asm/page.h */ #if defined(WANT_PAGE_VIRTUAL)void *virtual; /* Kernel virtual address (NULL if not kmapped, ie. highmem) */ #endif /* WANT_PAGE_VIRTUAL */ };

    - mapping:當(dāng)文件或者設(shè)備有內(nèi)存映射,那么文件的inode中有一個(gè)成員address_space,如果該page屬于這個(gè)文件那么mapping字段將指向這個(gè)address_space。

    - lru:根據(jù)page的替換策略,active_list或者inactive_list中的pages都可能被換出。該字段是active_list或者inacvtive_list的頭。

    - virtual:通常來(lái)講只有ZONE_NORMAL中的物理內(nèi)存才會(huì)被內(nèi)核直接映射到虛擬地址空間,而ZONE_HIGHMEM中的物理內(nèi)存需要調(diào)用kmap()來(lái)講物理地址轉(zhuǎn)成虛擬地址,通常只有固定數(shù)目的pages被kmap()轉(zhuǎn)換,如果當(dāng)前page被kmap映射了,則virtual字段記錄它的虛擬地址。

    - index:mapping中偏移。

    - flags:該字段表示描述page狀態(tài)的標(biāo)志位

    - count:當(dāng)前page正在被使用的次數(shù)。當(dāng)count為0時(shí),該page可能被釋放。只要大于0,那么當(dāng)前被一個(gè)或者多個(gè)進(jìn)程或者內(nèi)核在使用中。

    | bit名稱 | 描述 |

    PG_active | 當(dāng)一個(gè)page在avtive_list LRU中時(shí),該bit被置位。從active_list LRU中被刪除時(shí),該bit被清零。該bit標(biāo)記page當(dāng)前的熱度

    PG_arch_1 | 該bit位依據(jù)不同的架構(gòu)而不同的page狀態(tài)位。通常當(dāng)一個(gè)page第一次整體進(jìn)入page cache的時(shí)候該位被清零

    PG_checked | 僅僅被Ext2文件系統(tǒng)使用

    PG_dirty|該標(biāo)記位1時(shí)表示該page需要被刷新到磁盤上。磁盤文件對(duì)應(yīng)的page被寫后不會(huì)立即刷新到磁盤,該bit為來(lái)保證dirty page在刷新之前不被釋放

    PG_error|在磁盤IO過(guò)程中發(fā)生錯(cuò)誤,該bit為被置位

    PG_fs_1|該bit位保留給文件系統(tǒng)自己用。當(dāng)前只有NFS用該bit位來(lái)表示當(dāng)前page是否同遠(yuǎn)端server同步

    PG_highmem|高端內(nèi)存的pages不能被kernel直接映射,因此在mem_init()的時(shí)候high memory page該bit位被置位

    PG_launder|系統(tǒng)一般會(huì)先將該bit置位然后再調(diào)用writepage()函數(shù),當(dāng)系統(tǒng)想換出一個(gè)page,在掃描時(shí)如果發(fā)現(xiàn)一個(gè)page的該bit為被置位且PG_locked被置位,則系統(tǒng)會(huì)等待IO完成。

    PG_locked|當(dāng)進(jìn)行磁盤IO時(shí)該bit為必須置位,IO完成,該bit為清零

    PG_lru|如果一個(gè)page在active_list或者inactive_list中時(shí),該bit為置位

    PG_referenced|當(dāng)一個(gè)page被映射并且通過(guò)映射或者哈希表有訪問(wèn),則該bit為置位,其主要用作LRU list更新過(guò)程中。

    PG_slab|如果page正在被slab allocator使用,該bit為置位

    PG_skip|某些架構(gòu)中用該bit為來(lái)標(biāo)記跳過(guò)虛擬地址空間中沒有對(duì)應(yīng)物理內(nèi)存的部分

    PG_unused|該標(biāo)記位目前沒有使用

    PG_uptodate|當(dāng)一個(gè)page從磁盤中讀入內(nèi)容時(shí)沒有報(bào)錯(cuò),該bit為置位

    ### 映射 Pages 到 Zones

    在kernel2.4中,struct page中有個(gè)struct zone的指針,用來(lái)表示page輸入哪個(gè)zone,這是一種相當(dāng)浪費(fèi)的行為。因?yàn)楫?dāng)有幾千個(gè)page的時(shí)候,這一個(gè)小小的指針也會(huì)耗費(fèi)很多的memory。在kernel2.6中,struct page中的zone字段被刪除了,取而代之的是使用page->flags中的高ZONE_SHIFT(x86下是8)位來(lái)決定page屬于哪個(gè)zone。

    首先創(chuàng)建一個(gè)zone_table,mm/page_alloc.c:

    /** Used by page_zone() to look up the address of the struct zone whose* id is encoded in the upper bits of page->flags*/ struct zone *zone_table[1 << (ZONES_SHIFT + NODES_SHIFT)]; EXPORT_SYMBOL(zone_table);

    然后調(diào)用set_page_zone設(shè)置zone的ID。

    static inline void set_page_zone(struct page *page, unsigned long nodezone_num) {page->flags &= ~(~0UL << NODEZONE_SHIFT);page->flags |= nodezone_num << NODEZONE_SHIFT; }

    高端內(nèi)存--High Memory

    為什么要支持高端內(nèi)存?

    因?yàn)閗ernel所能使用的虛擬地址空間是有限的(一般只ZONE_NORMAL)。所以kernel需要支持High Memory的概念。在x86 32位系統(tǒng)中,對(duì)于高端內(nèi)存來(lái)說(shuō)有兩個(gè)閾值:4GB,64GB

    4GB:因?yàn)槲锢淼刂肥?2位,最大4GB。所以虛擬地址也是32位即虛擬地址空間是0 ~~ 4GB。由于內(nèi)核只能使用虛擬地址3G ~ 4G這一個(gè)G的虛擬地址,而這一個(gè)G的虛擬地址大部分被kernel從ZONE_NORMAL和ZONE_DMA直接線性映射所占用,如果系統(tǒng)想要訪問(wèn)更多的物理內(nèi)存就得從ZONE_HIGMEM中獲取物理頁(yè)框再調(diào)用kmap()將其映射到虛擬地址空間,從而讓高端物理內(nèi)存可以。

    64GB:Intel發(fā)明了PAE技術(shù)(Page Address Extension)讓32位系統(tǒng)可以使用更多的物理內(nèi)存。即 $2^{36}$ = 64GB

    PAE理論上讓處理器可以訪問(wèn)64GB物理內(nèi)存,但是實(shí)際上32位Linux系統(tǒng)中的進(jìn)程無(wú)法訪問(wèn)那么多物理內(nèi)存因?yàn)樘摂M地址空間仍然是4GB。

    再者來(lái)說(shuō),PAE不允許內(nèi)核自己使用如此多的內(nèi)存。一個(gè)struct page占用44字節(jié),而struct page所占用的的虛擬內(nèi)從空間是在內(nèi)核可用的虛擬地址空間內(nèi)。因此描述1GB的物理內(nèi)存,所需要的struct page就要占用11MB的虛擬地址空間,16GB的物理內(nèi)存描述就要占用176MB的虛擬地址空間,這會(huì)使得內(nèi)核所使用的虛擬地址空間更加緊張。

    因此在32位系統(tǒng)中要訪問(wèn)更多的物理內(nèi)存,就得內(nèi)核就得支持高端內(nèi)存的概念。

    所謂高端內(nèi)存就是指將內(nèi)核現(xiàn)行映射完成之后還有多余的物理內(nèi)存,通過(guò)動(dòng)態(tài)映射的方式將物理地址轉(zhuǎn)換為虛擬地址,從而使得這部分物理地址可用。這也僅僅是臨時(shí)可用,一個(gè)進(jìn)程也不能一次malloc 3G以上的內(nèi)存使用。

    高端內(nèi)存具體如何使用?

    例如socket中的buffer就是使用高端內(nèi)存,比如網(wǎng)絡(luò)上來(lái)了大量數(shù)據(jù),內(nèi)核先調(diào)用kmap()將一些page映射成虛擬地址,然后將網(wǎng)絡(luò)數(shù)據(jù)copy到物理內(nèi)存,然后調(diào)用kunmap()解除這段虛擬地址到物理地址的映射,此時(shí)內(nèi)核記錄的是struct page的地址來(lái)描述這些buff在哪里。

    當(dāng)上層應(yīng)用要讀取socket的數(shù)據(jù)的時(shí)候,kernel再次調(diào)用kmap()將之前存放網(wǎng)絡(luò)數(shù)據(jù)的page再次映射成虛擬地址,讓上層應(yīng)用將這些數(shù)據(jù)copy出去。

    總結(jié)

    以上是生活随笔為你收集整理的linux 初始化内存管理_Linux内存管理第二章 -- Describing Physical Memory的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。