linux内核工程师 3.02节 linux内核区域机制

 Linux可以支持大量的架构,所以需要用一种与架构无关的方式去描述内存。在linux的内存管理中,我们首先要明确的一个概念就是NUMA(Non-Uniform Memory Access,关于NUMA的介绍可以参考我前面的文章)。很多大型机器都采用NUMA架构,将内存和CPU分为很多组,每一组称为一个节点(node)。节点与节点之间的互相访问,会因为“距离”的不同导致不同的开销。Linux通过struct pglist_data这个结构体来描述节点,对于UMA架构,Linux同样会保留节点的概念,只是整个系统就是一个节点,只需要一个struct pglist_data来描述,它叫作contig_page_data.

struct pglist_data结构描述如下,现在只需了解其中的关键项即可

 

[cpp]  view plain  copy
  1. typedef struct pglist_data {  
  2.     struct zone node_zones[MAX_NR_ZONES];          /*节点中的管理区*/  
  3.     struct zonelist node_zonelists[MAX_ZONELISTS]; /*list中zone的顺序代表了分配内存的顺序,前者分配内存失败将会到后者的区域中分配内存*/  
  4.     int nr_zones;                    /*节点中管理区的数目,不一定为3个,有的节点中可能不存在ZONE_DMA*/  
  5. #ifdef CONFIG_FLAT_NODE_MEM_MAP          /* means !SPARSEMEM */  
  6.     struct page *node_mem_map;  
  7. #ifdef CONFIG_CGROUP_MEM_RES_CTLR  
  8.     struct page_cgroup *node_page_cgroup;  
  9. #endif  
  10. #endif  
  11.     struct bootmem_data *bdata;  
  12. #ifdef CONFIG_MEMORY_HOTPLUG  
  13.     /* 
  14.      * Must be held any time you expect node_start_pfn, node_present_pages 
  15.      * or node_spanned_pages stay constant.  Holding this will also 
  16.      * guarantee that any pfn_valid() stays that way. 
  17.      * 
  18.      * Nests above zone->lock and zone->size_seqlock. 
  19.      */  
  20.     spinlock_t node_size_lock;  
  21. #endif  
  22.     unsigned long node_start_pfn;     /*该节点的起始页框编号*/  
  23.     unsigned long node_present_pages; /* total number of physical pages */  
  24.     unsigned long node_spanned_pages; /* total size of physical page 
  25.                          range, including holes */  
  26.     int node_id;                      /*节点标识符,代表当前节点是系统中的第几个节点*/  
  27.     wait_queue_head_t kswapd_wait;    /*页换出进程使用的等待队列*/  
  28.     struct task_struct *kswapd;       /*指向页换出进程的进程描述符*/  
  29.     int kswapd_max_order;             /*kswapd将要创建的空闲块的大小取对数的值*/  




       每个节点的内存会被分为几个块,我们称之为管理区(zone),对于一个管理区,我们使用struct zone结构体来描述。管理区的类型可以分为ZONE_NORMAL,ZONE_DMA,ZONE_HIGHMEM三种。这三个管理区在物理内存上的布局为

ZONE_DMA:  0~16MB

ZONE_NORMAL:  16MB~896MB

ZONE_HIGHMEM:896MB~end   

 

struct zone结构描述如下:

[cpp]  view plain  copy
  1. struct zone {  
  2.     /* Fields commonly accessed by the page allocator */  
  3.   
  4.     /* zone watermarks, access with *_wmark_pages(zone) macros */  
  5.     unsigned long watermark[NR_WMARK];/*该管理区的三个水平线值,min,low,high*/  
  6.   
  7.     /* 
  8.      * When free pages are below this point, additional steps are taken 
  9.      * when reading the number of free pages to avoid per-cpu counter 
  10.      * drift allowing watermarks to be breached 
  11.      */  
  12.     unsigned long percpu_drift_mark;  
  13.   
  14.     /* 
  15.      * We don't know if the memory that we're going to allocate will be freeable 
  16.      * or/and it will be released eventually, so to avoid totally wasting several 
  17.      * GB of ram we must reserve some of the lower zone memory (otherwise we risk 
  18.      * to run OOM on the lower zones despite there's tons of freeable ram 
  19.      * on the higher zones). This array is recalculated at runtime if the 
  20.      * sysctl_lowmem_reserve_ratio sysctl changes. 
  21.      */  
  22.     unsigned long       lowmem_reserve[MAX_NR_ZONES];  /*每个管理区必须保留的页框数*/  
  23.   
  24. #ifdef CONFIG_NUMA           /*如果定义了NUMA*/  
  25.     int node;           /*该管理区所属节点的节点号*/  
  26.     /* 
  27.      * zone reclaim becomes active if more unmapped pages exist. 
  28.      */  
  29.     unsigned long       min_unmapped_pages;  /*当可回收的页面数大于该变量时,管理区将回收页面*/  
  30.     unsigned long       min_slab_pages;      /*同上,只不过该标准用于slab回收页面中*/  
  31.     struct per_cpu_pageset  *pageset[NR_CPUS];   /*每个CPU使用的页面缓存*/  
  32. #else  
  33.     struct per_cpu_pageset  pageset[NR_CPUS];  
  34. #endif  
  35.     /* 
  36.      * free areas of different sizes 
  37.      */  
  38.     spinlock_t      lock;       /*保护该管理区的自旋锁*/  
  39. #ifdef CONFIG_MEMORY_HOTPLUG  
  40.     /* see spanned/present_pages for more description */  
  41.     seqlock_t       span_seqlock;  
  42. #endif  
  43.     struct free_area    free_area[MAX_ORDER];/*标识出管理区中的空闲页框块*/  
  44.   
  45. #ifndef CONFIG_SPARSEMEM  
  46.     /* 
  47.      * Flags for a pageblock_nr_pages block. See pageblock-flags.h. 
  48.      * In SPARSEMEM, this map is stored in struct mem_section 
  49.      */  
  50.     unsigned long       *pageblock_flags;  
  51. #endif /* CONFIG_SPARSEMEM */  
  52.   
  53.   
  54.     ZONE_PADDING(_pad1_)  
  55.   
  56.     /* Fields commonly accessed by the page reclaim scanner */  
  57.     spinlock_t      lru_lock;     
  58.     struct zone_lru {  
  59.         struct list_head list;  
  60.     } lru[NR_LRU_LISTS];  
  61.       
  62.     struct zone_reclaim_stat reclaim_stat; /*页面回收的状态*/  
  63.   
  64.     /*管理区回收页框时使用的计数器,记录到上一次回收,一共扫过的页框数*/  
  65.     unsigned long       pages_scanned;     /* since last reclaim */  
  66.     unsigned long       flags;         /* zone flags, see below */  
  67.   
  68.     /* Zone statistics */  
  69.     atomic_long_t       vm_stat[NR_VM_ZONE_STAT_ITEMS];  
  70.   
  71.     /* 
  72.      * prev_priority holds the scanning priority for this zone.  It is 
  73.      * defined as the scanning priority at which we achieved our reclaim 
  74.      * target at the previous try_to_free_pages() or balance_pgdat() 
  75.      * invokation. 
  76.      * 
  77.      * We use prev_priority as a measure of how much stress page reclaim is 
  78.      * under - it drives the swappiness decision: whether to unmap mapped 
  79.      * pages. 
  80.      * 
  81.      * Access to both this field is quite racy even on uniprocessor.  But 
  82.      * it is expected to average out OK. 
  83.      */  
  84.     int prev_priority;  
  85.   
  86.     /* 
  87.      * The target ratio of ACTIVE_ANON to INACTIVE_ANON pages on 
  88.      * this zone's LRU.  Maintained by the pageout code. 
  89.      */  
  90.     unsigned int inactive_ratio;  
  91.   
  92.   
  93.     ZONE_PADDING(_pad2_)  
  94.     /* Rarely used or read-mostly fields */  
  95.   
  96.     /* 
  97.      * wait_table       -- the array holding the hash table 
  98.      * wait_table_hash_nr_entries   -- the size of the hash table array 
  99.      * wait_table_bits  -- wait_table_size == (1 << wait_table_bits) 
  100.      * 
  101.      * The purpose of all these is to keep track of the people 
  102.      * waiting for a page to become available and make them 
  103.      * runnable again when possible. The trouble is that this 
  104.      * consumes a lot of space, especially when so few things 
  105.      * wait on pages at a given time. So instead of using 
  106.      * per-page waitqueues, we use a waitqueue hash table. 
  107.      * 
  108.      * The bucket discipline is to sleep on the same queue when 
  109.      * colliding and wake all in that wait queue when removing. 
  110.      * When something wakes, it must check to be sure its page is 
  111.      * truly available, a la thundering herd. The cost of a 
  112.      * collision is great, but given the expected load of the 
  113.      * table, they should be so rare as to be outweighed by the 
  114.      * benefits from the saved space. 
  115.      * 
  116.      * __wait_on_page_locked() and unlock_page() in mm/filemap.c, are the 
  117.      * primary users of these fields, and in mm/page_alloc.c 
  118.      * free_area_init_core() performs the initialization of them. 
  119.      */  
  120.     wait_queue_head_t   * wait_table;   /*进程等待队列的散列表,这些进程正在等待管理区中的某页*/  
  121.     unsigned long       wait_table_hash_nr_entries;   /*散列表数组的大小*/  
  122.     unsigned long       wait_table_bits;              /*散列表数组的大小对2取log的结果*/  
  123.   
  124.     /* 
  125.      * Discontig memory support fields. 
  126.      */  
  127.     struct pglist_data  *zone_pgdat;              /*管理区所属节点*/  
  128.     /* zone_start_pfn == zone_start_paddr >> PAGE_SHIFT */  
  129.     unsigned long       zone_start_pfn; /*管理区的起始页框号*/  
  130.   
  131.     /* 
  132.      * zone_start_pfn, spanned_pages and present_pages are all 
  133.      * protected by span_seqlock.  It is a seqlock because it has 
  134.      * to be read outside of zone->lock, and it is done in the main 
  135.      * allocator path.  But, it is written quite infrequently. 
  136.      * 
  137.      * The lock is declared along with zone->lock because it is 
  138.      * frequently read in proximity to zone->lock.  It's good to 
  139.      * give them a chance of being in the same cacheline. 
  140.      */  
  141.     unsigned long       spanned_pages;  /*管理区的大小,包括洞*/  
  142.     unsigned long       present_pages;  /*管理区的大小,不包括洞*/  
  143.   
  144.     /* 
  145.      * rarely used fields: 
  146.      */  
  147.     const char      *name; /*指向管理区的名称,为"DMA","NORMAL"或"HighMem"*/  
  148. }  

 

物理内存中的每个页都会有一个与之关联的struct page结构来对其进行描述和跟踪,其结构描述如下

[cpp]  view plain  copy
  1. struct page {  
  2.     unsigned long flags;        /* Atomic flags, some possibly 
  3.                      * updated asynchronously */  
  4.     atomic_t _count;        /* Usage count, see below. */  
  5.     union {  
  6.         atomic_t _mapcount; /* Count of ptes mapped in mms, 
  7.                      * to show when page is mapped 
  8.                      * & limit reverse map searches. 
  9.                      */  
  10.         struct {        /* SLUB */  
  11.             u16 inuse;  
  12.             u16 objects;  
  13.         };  
  14.     };  
  15.     union {  
  16.         struct {  
  17.         unsigned long private;      /* Mapping-private opaque data: 
  18.                          * usually used for buffer_heads 
  19.                          * if PagePrivate set; used for 
  20.                          * swp_entry_t if PageSwapCache; 
  21.                          * indicates order in the buddy 
  22.                          * system if PG_buddy is set. 
  23.                          */  
  24.         struct address_space *mapping;  /* If low bit clear, points to 
  25.                          * inode address_space, or NULL. 
  26.                          * If page mapped as anonymous 
  27.                          * memory, low bit is set, and 
  28.                          * it points to anon_vma object: 
  29.                          * see PAGE_MAPPING_ANON below. 
  30.                          */  
  31.         };  
  32. #if USE_SPLIT_PTLOCKS  
  33.         spinlock_t ptl;  
  34. #endif  
  35.         struct kmem_cache *slab;    /* SLUB: Pointer to slab */  
  36.         struct page *first_page;    /* Compound tail pages */  
  37.     };  
  38.     union {  
  39.         pgoff_t index;      /* Our offset within mapping. */  
  40.         void *freelist;     /* SLUB: freelist req. slab lock */  
  41.     };  
  42.     struct list_head lru;       /* Pageout list, eg. active_list 
  43.                      * protected by zone->lru_lock ! 
  44.                      */  
  45.     /* 
  46.      * On machines where all RAM is mapped into kernel address space, 
  47.      * we can simply calculate the virtual address. On machines with 
  48.      * highmem some memory is mapped into kernel virtual memory 
  49.      * dynamically, so we need a place to store that address. 
  50.      * Note that this field could be 16 bits on x86 ... ;) 
  51.      * 
  52.      * Architectures with slow multiplication can define 
  53.      * WANT_PAGE_VIRTUAL in asm/page.h 
  54.      */  
  55. #if defined(WANT_PAGE_VIRTUAL)  
  56.     void *virtual;          /* Kernel virtual address (NULL if 
  57.                        not kmapped, ie. highmem) */  
  58. #endif /* WANT_PAGE_VIRTUAL */  
  59. #ifdef CONFIG_WANT_PAGE_DEBUG_FLAGS  
  60.     unsigned long debug_flags;  /* Use atomic bitops on this */  
  61. #endif  
  62.   
  63. #ifdef CONFIG_KMEMCHECK  
  64.     /* 
  65.      * kmemcheck wants to track the status of each byte in a page; this 
  66.      * is a pointer to such a status block. NULL if not tracked. 
  67.      */  
  68.     void *shadow;  
  69. #endif  
  70. };  

 

Node,Zone和Page的关系可以用下图来描述

 

至此,已经对内存管理中的这三个关键数据结构有了一个感性的认识,在后面将会结合具体的代码来逐步深入,选择的代码版本为2.6.32.59~
 


猜你喜欢

转载自blog.csdn.net/zjy900507/article/details/80681073