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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

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

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

首先來描述幾個名詞:

  • NUMA:Non-Uniform Memory Access即內存非一致性訪問.在很多巨星機上內存被分成了多個區域,CPU訪問不同的內存區域耗費的時間是不同的.但在同一區域內,CPU訪問這些內存的時間是一致的。
  • UMA:Uniform Memory Access即內存一致性訪問,即所CPU訪問所有內存的耗時都是一致的。
  • 3. node:NUMA架構下的每一個內存區域稱作是node,node用struct pglist_data來描述。所有的node在Linux內核中用一個全局鏈表pgdat_list關聯起來,pg_data_t->pgdat_next指向下一個區域。

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

    5. page:Linux內核對每一個物理內存頁框的描述。

    node和zone的關系如下圖:

    Nodes

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

    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:當前node中的zones:ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM

    node_zonelists:該數組決定了當前node中分配物理內存時,選擇zone的順序.該數組的初始化由mm/page_alloc.c中的build_zonelists()來完成。build_zonelists()由free_area_init_core()調用觸發。一般狀況下當從ZONE_HIGHMEM中分配內存失敗時,會再嘗試從ZONE_NORMAL或者ZONE_DMA中分配。

    node_mem_map:該指針表示當前node中的物理頁框數字中的第一個頁框描述符struct page的地址。在內核中有些地方也可以直接使用全局數組mem_map來訪問獲取node的第一個page地址,實現node_mem_map相同的功能。

    node_start_paddr:當前node的物理地址的起始地址。該字段類型是一個unsigned long型,有的時候可能會超長,一個比較好的頒發是在此字段中中記錄物理頁框號(Page Frame Number,PFN)。PFN = page_phys_addr >> PAGE_SHIFT

    node_start_mapnr:當前node中起始物理地址在全局數組mem_map中的偏移。

    node_size:當前node中page的總數。

    系統中維護的所有node都在一個全局列表pgdat_list中。該list由init_bootmem_core()函數初始化。

    Zones

    Linux內核中每一個zone由struct zone結構來描述。該結構主要使用來統計page的使用和釋放信息。其定義如下,接下來再看下重點成員:

    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:當前zone中free page的總個數

    - pages_min,pages_low,page_high:該zone中的水位線,具體在下一小節進行說明。

    - free_area:buddy allocator使用的free area bitmaps

    - wait_table:等待某個page被釋放的進程的等待隊列鏈表。這個隊列對于wait_on_page()和unlock_page()函數來說非常重要。

    - wait_table_size:wait_table中等待隊列的個數。

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

    - zone_pgdat:指向當前zone所屬的node

    - zone_mem_map:當前zone中的第一個page的地址,該page輸入全局數組mem_map

    - size:當前zone中pages 的個數

    Zone初始化

    當內核的頁表有page_init()建立好之后,就開會初始化zone。每種硬件架構執行zone初始化的時候可能稍有不同,但是最終目標是相同的。每種架構初始化zone的流程中的最終目標是決定傳入什么參數值到UMA架構的初始化函數free_area_init()或者NUMA架構的初始化函數free_area_init_node()中。下面來看下參數的含義:

    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的邏輯標識符。

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

    - zones_size:包含每個zone的page的個數的數組

    - node_start_pfn:當前node的起始PFN

    - zholes_size:包含每個zone中memory holes的個數的數組。

    無論是free_area_init()還是free_area_init_node()都會調用free_area_init_core()來真正初始化每個struct zone中的成員。在做zone初始化的時候,系統無法知道每個zone中有多少可用的page,這些信息知道boot memory allocator退休之后才會知道。

    mem_map初始化

    mem_map是在系統啟動階段被創建。在NUMA系統中,全局數組mem_map被當做是一個虛擬的數組其起始位置為PAGE_OFFSET(一般就是指虛擬地址3G的位置)。系統中的每個node初始化的過程中調用free_area_init_node()來為這個虛擬數組分配填充mem_map的一部分內容。而在UMA系統中,因為只有一個node,所以其node為全局變量contig_page_data,而全局變量mem_map就是這個contig_page_data中的ode_mem_map。

    對于UMA架構來說,核心函數free_area_core()將為當前初始化的node分配一個本地的lmem_map,lmem_map是從boot memory allocator中使用alloc_bootmem_node()來進行分配。該新分配的lmem_map將會成為全局的mem_map。但NUMA系統中與此有稍微不同。

    對于NUMA架構來說,核心函數free_area_core()將從node節點自己的內存中分配lmem_map,而全部變量mem_map不會顯式的分配,而是將其設定為PAGE_OFFSET的虛擬地址上,其被當做是一個虛擬的數組。而lmem_map被存儲在node中的node_mem_map,pg_data_t->node_mem_map,其在虛擬數組mem_map的某個位置上。當node和zone被初始化完整之后,mem_map將會被當成一個真正的數組了。

    Pages

    page定義

    系統中的每一個物理頁框都有一個相應地struct page,其用來持續跟蹤物理頁框的狀態。其定義如下:

    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:當文件或者設備有內存映射,那么文件的inode中有一個成員address_space,如果該page屬于這個文件那么mapping字段將指向這個address_space。

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

    - virtual:通常來講只有ZONE_NORMAL中的物理內存才會被內核直接映射到虛擬地址空間,而ZONE_HIGHMEM中的物理內存需要調用kmap()來講物理地址轉成虛擬地址,通常只有固定數目的pages被kmap()轉換,如果當前page被kmap映射了,則virtual字段記錄它的虛擬地址。

    - index:mapping中偏移。

    - flags:該字段表示描述page狀態的標志位

    - count:當前page正在被使用的次數。當count為0時,該page可能被釋放。只要大于0,那么當前被一個或者多個進程或者內核在使用中。

    | bit名稱 | 描述 |

    PG_active | 當一個page在avtive_list LRU中時,該bit被置位。從active_list LRU中被刪除時,該bit被清零。該bit標記page當前的熱度

    PG_arch_1 | 該bit位依據不同的架構而不同的page狀態位。通常當一個page第一次整體進入page cache的時候該位被清零

    PG_checked | 僅僅被Ext2文件系統使用

    PG_dirty|該標記位1時表示該page需要被刷新到磁盤上。磁盤文件對應的page被寫后不會立即刷新到磁盤,該bit為來保證dirty page在刷新之前不被釋放

    PG_error|在磁盤IO過程中發生錯誤,該bit為被置位

    PG_fs_1|該bit位保留給文件系統自己用。當前只有NFS用該bit位來表示當前page是否同遠端server同步

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

    PG_launder|系統一般會先將該bit置位然后再調用writepage()函數,當系統想換出一個page,在掃描時如果發現一個page的該bit為被置位且PG_locked被置位,則系統會等待IO完成。

    PG_locked|當進行磁盤IO時該bit為必須置位,IO完成,該bit為清零

    PG_lru|如果一個page在active_list或者inactive_list中時,該bit為置位

    PG_referenced|當一個page被映射并且通過映射或者哈希表有訪問,則該bit為置位,其主要用作LRU list更新過程中。

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

    PG_skip|某些架構中用該bit為來標記跳過虛擬地址空間中沒有對應物理內存的部分

    PG_unused|該標記位目前沒有使用

    PG_uptodate|當一個page從磁盤中讀入內容時沒有報錯,該bit為置位

    ### 映射 Pages 到 Zones

    在kernel2.4中,struct page中有個struct zone的指針,用來表示page輸入哪個zone,這是一種相當浪費的行為。因為當有幾千個page的時候,這一個小小的指針也會耗費很多的memory。在kernel2.6中,struct page中的zone字段被刪除了,取而代之的是使用page->flags中的高ZONE_SHIFT(x86下是8)位來決定page屬于哪個zone。

    首先創建一個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);

    然后調用set_page_zone設置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; }

    高端內存--High Memory

    為什么要支持高端內存?

    因為kernel所能使用的虛擬地址空間是有限的(一般只ZONE_NORMAL)。所以kernel需要支持High Memory的概念。在x86 32位系統中,對于高端內存來說有兩個閾值:4GB,64GB

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

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

    PAE理論上讓處理器可以訪問64GB物理內存,但是實際上32位Linux系統中的進程無法訪問那么多物理內存因為虛擬地址空間仍然是4GB。

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

    因此在32位系統中要訪問更多的物理內存,就得內核就得支持高端內存的概念。

    所謂高端內存就是指將內核現行映射完成之后還有多余的物理內存,通過動態映射的方式將物理地址轉換為虛擬地址,從而使得這部分物理地址可用。這也僅僅是臨時可用,一個進程也不能一次malloc 3G以上的內存使用。

    高端內存具體如何使用?

    例如socket中的buffer就是使用高端內存,比如網絡上來了大量數據,內核先調用kmap()將一些page映射成虛擬地址,然后將網絡數據copy到物理內存,然后調用kunmap()解除這段虛擬地址到物理地址的映射,此時內核記錄的是struct page的地址來描述這些buff在哪里。

    當上層應用要讀取socket的數據的時候,kernel再次調用kmap()將之前存放網絡數據的page再次映射成虛擬地址,讓上層應用將這些數據copy出去。

    總結

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

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。