当前位置:   article > 正文

Linux内存管理(2):内存描述_config_memory linux kernel

config_memory linux kernel
    linux内存管理建立在基本的分页机制基础上,在linux内核中RAM的某些部分将会永久的分配给内核,并用来存放内核代码以及静态内核数据结构。RAM的其余部分称为动态内存,这不仅是进程所需的宝贵资源,也是内核本身所需的宝贵资源。实际上,整个系统的性能取决于如何有效地管理动态内存。因此,现在所有多任务操作系统都在经历优化对动态内存的使用,也就是说,尽可能做到当要时分配,不需要时释放。
    内存管理是os中最复杂的管理机制之一。linux中采用了很多有效的管理方法,包括页表管理、高端内存(临时映射区、固定映射区、永久映射区、非连续内存区)管理、为减小外部碎片的伙伴系统、为减小内部碎片的slab机制、伙伴系统未建立之前的页面分配制度以及紧急内存管理等等。

    linux使用于广泛的体系结构,因此需要用一种与体系结构无关的方式来描述内存。linux用VM描述和管理内存。在VM中使用的普遍概念就是非一致内存访问。对于大型机器而言,内存会分成许多簇,依据簇与处理器“距离”的不同,访问不同的簇会有不同的代价。每个簇都被认为是一个节点(pg_data_t),每个节点被分成很多的称为管理区(zone)的块,用于表示内存中的某个范围。zone的类型除了ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM以外,从linux2.6.32开始引入了ZONE_MOVABLE,用于适应大块连续内存的分配。每个物理页面由一个page结构体描述,所有的页结构都存储在一个全局的mem_map数组中(非平板模式),该数组通常存放在ZONE_NORMAL内存区域的首部,或者就在内存系统中为装入内核映像而预留的区域之后。内存描述的层次结构为pg_data_t--->zone--->mem_map数组(ZONE_XXX类型)--->page,如下图。下面的以2.6.32.45的内核代码为参考来介绍。

图1 内存描述的层次结构

    1、节点:pg_data_t

    内存的每个节点都有pg_data_t描述,在分配一个页面时,linux采用节点局部分配的策略,从最靠近运行中的CPU的节点分配内存。由于进程往往是在同一个CPU上运行,因此从当前节点得到的内存很可能被用到。pg_data_t在include/linux/mmzone.h中,如下:

  1. /*
  2. * pg_data_t结构用在带有CONFIG_DISCONTIGMEM编译选项的机器中(最新的NUMA机器),
  3. * 以表示比zone结构更高一层次的内存区域。
  4. * 在NUMA机器上,每个NUMA节点由一个pg_data_t来描述它的内存布局。内存使用统计和
  5. * 页面交换数据结构由每个zone区域来维护
  6. */
  7. struct bootmem_data;
  8. typedef struct pglist_data {
  9. /* 该节点内的内存区。可能的区域类型用zone_type表示 */
  10. struct zone node_zones[MAX_NR_ZONES];
  11. /* 该节点的备用内存区。当节点没有可用内存时,就从备用区中分配内存 */
  12. struct zonelist node_zonelists[MAX_ZONELISTS];
  13. /* 可用内存区数目,即node_zones数据中保存的最后一个有效区域的索引 */
  14. int nr_zones;
  15. #ifdef CONFIG_FLAT_NODE_MEM_MAP /* means !SPARSEMEM */
  16. /* 在平坦型的内存模型中,它指向本节点第一个页面的描述符 */
  17. struct page *node_mem_map;
  18. #ifdef CONFIG_CGROUP_MEM_RES_CTLR
  19. /* cgroup相关 */
  20. struct page_cgroup *node_page_cgroup;
  21. #endif
  22. #endif
  23. /* 在内存子系统初始化以前,即boot阶段也需要进行内存管理。
  24. * 此结构用于这个阶段的内存管理。
  25. */
  26. struct bootmem_data *bdata;
  27. #ifdef CONFIG_MEMORY_HOTPLUG
  28. /* 当系统支持内存热插拨时,这个锁用于保护本结构中的与节点大小相关的字段。
  29. * 当你希望node_start_pfn,node_present_pages,node_spanned_pages仍保持常量时,
  30. * 需要持有该锁。
  31. */
  32. spinlock_t node_size_lock;
  33. #endif
  34. unsigned long node_start_pfn; /*起始页面帧号,指出该节点在全局mem_map中的偏移*/
  35. unsigned long node_present_pages; /* 物理页的总数 */
  36. unsigned long node_spanned_pages; /* 物理页范围的跨度,包括holes */
  37. int node_id; /* 节点编号 */
  38. /* 等待该节点内的交换守护进程的等待队列。将节点中的页帧换出时会用到 */
  39. wait_queue_head_t kswapd_wait;
  40. /* 负责该节点的交换守护进程 */
  41. struct task_struct *kswapd;
  42. /* 由页交换子系统使用,定义要释放的区域大小 */
  43. int kswapd_max_order;
  44. } pg_data_t;
    该结构的主要数据有内存区、备用内存区、可用内存区计数、锁、物理页总数、物理页范围跨度、所属交换守护进程等。一个节点通过node_zones数组有维护多个zone管理区。
     2、管理区:zone
    管理区用于跟踪诸如页面使用情况统计数,空闲区域信息和锁信息等。每个管理区由一个zone结构体描述,管理区的类型由zone_type描述,都在include/linux/mmzone.h中。如下:
  1. enum zone_type {
  2. #ifdef CONFIG_ZONE_DMA
  3. /*
  4. * ZONE_DMA is used when there are devices that are not able
  5. * to do DMA to all of addressable memory (ZONE_NORMAL). Then we
  6. * carve out the portion of memory that is needed for these devices.
  7. * The range is arch specific.
  8. *
  9. * Some examples
  10. *
  11. * Architecture Limit
  12. * ---------------------------
  13. * parisc, ia64, sparc <4G
  14. * s390 <2G
  15. * arm Various
  16. * alpha Unlimited or 0-16MB.
  17. *
  18. * i386, x86_64 and multiple other arches
  19. * <16M.
  20. */
  21. ZONE_DMA,
  22. #endif
  23. #ifdef CONFIG_ZONE_DMA32
  24. /*
  25. * x86_64 needs two ZONE_DMAs because it supports devices that are
  26. * only able to do DMA to the lower 16M but also 32 bit devices that
  27. * can only do DMA areas below 4G.
  28. */
  29. ZONE_DMA32,
  30. #endif
  31. /*
  32. * Normal addressable memory is in ZONE_NORMAL. DMA operations can be
  33. * performed on pages in ZONE_NORMAL if the DMA devices support
  34. * transfers to all addressable memory.
  35. */
  36. ZONE_NORMAL,
  37. #ifdef CONFIG_HIGHMEM
  38. /*
  39. * A memory area that is only addressable by the kernel through
  40. * mapping portions into its own address space. This is for example
  41. * used by i386 to allow the kernel to address the memory beyond
  42. * 900MB. The kernel will set up special mappings (page
  43. * table entries on i386) for each page that the kernel needs to
  44. * access.
  45. */
  46. ZONE_HIGHMEM,
  47. #endif
  48. ZONE_MOVABLE,
  49. __MAX_NR_ZONES
  50. };
    管理区类型介绍:
    (1)ZONE_DMA:用在当有设备不能通过DMA访问整个可寻址内存(ZONE_NORMAL)的情况下。这时我们就要为这些设备专门开辟出一段内存,通常是低端内存区域。ZONE_DMA的内存范围与体系结构有关,parisc、ia64以及sparc中是小于4G;s390是小于2G;arm中是可变的多种多样的;alpha中是无限或者0-16MB;i386、x86_64以及其他很多体系结构是小于16MB(0-16MB)。
    (2)ZONE_DMA32:注意x86_64需要两个ZONE_DMA区域,因为它既支持只能访问16MB以下DMA区域的设备,也支持只能访问4GB以下DMA区域的32位设备,ZONE_DMA32针对后一种情况。
    (3)ZONE_NORMAL:正常的可访问内存。如果DMA设备能支持传输数据到整个可访问内存,则DMA操作也能在ZONE_NORMAL类型的页面上进行。
    (4)ZONE_HIGHMEM:映射到内核代码本身的内核地址空间,一般是高端内存区域,它只能由内核访问,用户空间访问不到。所有的内核操作都只能使用这个内存区域来进行,因此这是对性能至关重要的区域。例如i386允许内核访问超过900MB的内存,对每个内核需要访问的页面,内核将设置特别的映射项(i386上的页表项)。
    (5)ZONE_MOVABLE:这是一个伪内存段。为了防止形成物理内存碎片,可以将虚拟地址对应的物理地址进行迁移,使多个碎片合并成一块连续的大内存。ZONE_MOVABLE类型用于适应大块连续内存的分配。
  1. struct zone {
  2. /* 被页面分配器访问的通用域 */
  3. /* 本管理区的三个水线值:高水线(比较充足)、低水线、MIN水线。会被*_wmark_pages(zone)宏访问 */
  4. unsigned long watermark[NR_WMARK];
  5. /* 当可用页数在本水线值以下时,在读取可用页计数值时,需要增加额外的工作以避免每个CPU的计数器
  6. * 漂移导致水线值被打破
  7. */
  8. unsigned long percpu_drift_mark;
  9. /* 我们不知道即将分配的内存是否可用,以及最终是否会被释放,因此为了避免浪费几GB的RAM,我们
  10. * 必须额外保留一些低端区域的内存(如DMA区域)供驱动使用。否则我们会面临在低端区域内出现
  11. * OOM(Out of Memory)的风险,尽管这时高端区域还有大量可用的RAM。本字段是指从上级内存区
  12. * 退到回内存区时,需要额外保留的内存数量。如果在运行时sysctl_lowmem_reserve_ratio控制
  13. * 改变,它会被重新计算
  14. */
  15. unsigned long lowmem_reserve[MAX_NR_ZONES];
  16. #ifdef CONFIG_NUMA
  17. int node; /* 所属的NUMA节点 */
  18. /* 未映射的页(即可回收的页)超过此值,将进行页面回收 */
  19. unsigned long min_unmapped_pages;
  20. /* 管理区中用于slab的可回收页大于此值时,将回收slab中的缓存页 */
  21. unsigned long min_slab_pages;
  22. /*
  23. * 每CPU的页面缓存。
  24. * 当分配单个页面时,首先从该缓存中分配页面。这样可以:
  25. * 避免使用全局的锁
  26. * 避免同一个页面反复被不同的CPU分配,引起缓存页的失效。
  27. * 避免将管理区中的大块分割成碎片。
  28. */
  29. struct per_cpu_pageset *pageset[NR_CPUS];
  30. #else
  31. struct per_cpu_pageset pageset[NR_CPUS];
  32. #endif
  33. /* 该锁用于保护伙伴系统数据结构。即保护free_area相关数据 */
  34. spinlock_t lock;
  35. #ifdef CONFIG_MEMORY_HOTPLUG
  36. /* 用于保护spanned/present_pages等变量。这些变量几乎不会发生变化,除非发生了内存热插拨操作。
  37. * 这几个变量并不被lock字段保护。并且主要用于读,因此使用读写锁 */
  38. seqlock_t span_seqlock;
  39. #endif
  40. /* 伙伴系统的主要变量。这个数组定义了11个队列,每个队列中的元素都是大小为2^n的页面 */
  41. struct free_area free_area[MAX_ORDER];
  42. #ifndef CONFIG_SPARSEMEM
  43. /* 本管理区里的pageblock_nr_pages块标志数组,参考pageblock-flags.h
  44. * 在SPARSEMEM中,本映射存储在结构mem_section中 */
  45. unsigned long *pageblock_flags;
  46. #endif /* CONFIG_SPARSEMEM */
  47. /* 填充的未用字段,确保后面的字段是缓存行对齐的 */
  48. ZONE_PADDING(_pad1_)
  49. /* 被页面回收扫描器访问的通用域 */
  50. /*
  51. * lru相关的字段用于内存回收。这个锁用于保护这几个回收相关的字段。
  52. * lru用于确定哪些字段是活跃的,哪些不是活跃的,并据此确定应当被写回到磁盘以释放内存。
  53. */
  54. spinlock_t lru_lock;
  55. /* 匿名活动页、匿名不活动页、文件活动页、文件不活动页链表头 */
  56. struct zone_lru {
  57. struct list_head list;
  58. } lru[NR_LRU_LISTS];
  59. struct zone_reclaim_stat reclaim_stat; /* 页面回收状态 */
  60. /* 自从最后一次回收页面以来,扫过的页面数 */
  61. unsigned long pages_scanned;
  62. unsigned long flags; /* 管理区标志,参考下面 */
  63. /* Zone statistics */
  64. atomic_long_t vm_stat[NR_VM_ZONE_STAT_ITEMS];
  65. /*
  66. * prev_priority holds the scanning priority for this zone. It is
  67. * defined as the scanning priority at which we achieved our reclaim
  68. * target at the previous try_to_free_pages() or balance_pgdat()
  69. * invokation.
  70. *
  71. * We use prev_priority as a measure of how much stress page reclaim is
  72. * under - it drives the swappiness decision: whether to unmap mapped
  73. * pages.
  74. *
  75. * Access to both this field is quite racy even on uniprocessor. But
  76. * it is expected to average out OK.
  77. */
  78. int prev_priority;
  79. /*
  80. * The target ratio of ACTIVE_ANON to INACTIVE_ANON pages on
  81. * this zone's LRU. Maintained by the pageout code.
  82. */
  83. unsigned int inactive_ratio;
  84. /* 为cache对齐 */
  85. ZONE_PADDING(_pad2_)
  86. /* Rarely used or read-mostly fields */
  87. /*
  88. * wait_table -- the array holding the hash table
  89. * wait_table_hash_nr_entries -- the size of the hash table array
  90. * wait_table_bits -- wait_table_size == (1 << wait_table_bits)
  91. *
  92. * The purpose of all these is to keep track of the people
  93. * waiting for a page to become available and make them
  94. * runnable again when possible. The trouble is that this
  95. * consumes a lot of space, especially when so few things
  96. * wait on pages at a given time. So instead of using
  97. * per-page waitqueues, we use a waitqueue hash table.
  98. *
  99. * The bucket discipline is to sleep on the same queue when
  100. * colliding and wake all in that wait queue when removing.
  101. * When something wakes, it must check to be sure its page is
  102. * truly available, a la thundering herd. The cost of a
  103. * collision is great, but given the expected load of the
  104. * table, they should be so rare as to be outweighed by the
  105. * benefits from the saved space.
  106. *
  107. * __wait_on_page_locked() and unlock_page() in mm/filemap.c, are the
  108. * primary users of these fields, and in mm/page_alloc.c
  109. * free_area_init_core() performs the initialization of them.
  110. */
  111. wait_queue_head_t * wait_table;
  112. unsigned long wait_table_hash_nr_entries;
  113. unsigned long wait_table_bits;
  114. /*
  115. * Discontig memory support fields.
  116. */
  117. struct pglist_data *zone_pgdat; /* 本管理区所属的节点 */
  118. /* zone_start_pfn == zone_start_paddr >> PAGE_SHIFT */
  119. unsigned long zone_start_pfn; /* 管理区的页面在mem_map中的偏移 */
  120. /*
  121. * zone_start_pfn, spanned_pages and present_pages are all
  122. * protected by span_seqlock. It is a seqlock because it has
  123. * to be read outside of zone->lock, and it is done in the main
  124. * allocator path. But, it is written quite infrequently.
  125. *
  126. * The lock is declared along with zone->lock because it is
  127. * frequently read in proximity to zone->lock. It's good to
  128. * give them a chance of being in the same cacheline.
  129. */
  130. unsigned long spanned_pages; /* total size, including holes */
  131. unsigned long present_pages; /* amount of memory (excluding holes) */
  132. const char *name; /* 很少使用的域 */
  133. } ____cacheline_internodealigned_in_smp;
    zone结构中的字段主要分两大类,一类是被页面分配器访问的字段,有水线值、保留的DMA内存区域数量、所属NUMA节点、未映射页数、slab中缓存页数、每个CPU的缓存页面集、伙伴系统可用区域数组free_area、页面标志数组等。一类是被页面回收器访问的字段,有LRU链表(用于LRU页面回收算法)、页面回收统计信息、所属的pglist_data节点、页面在mem_map中的偏移等。
     3、物理页面:page
    系统中每个物理页面都有一个相关联的page用于记录该页面的状态。在include/linux/mm_types.h中,如下:
  1. /*
  2. * 系统中每个物理页面有一个相关联的page结构,用于记录该页面的状态。注意虽然当该页面是
  3. * 一个缓存页时,rmap结构能告诉我们谁正在映射它,但我们并没有一般的方法来跟踪哪个进程正在使用该页面
  4. */
  5. struct page {
  6. unsigned long flags; /* 原子标志,一些可以会被异步更新 */
  7. atomic_t _count; /* 使用计数,参考下面 */
  8. union {
  9. atomic_t _mapcount; /* 在mms中映射的ptes计数,用于表明页面什么时候被映射,
  10. * 并且限制反向映射搜索
  11. */
  12. struct { /* SLUB */
  13. u16 inuse;
  14. u16 objects;
  15. };
  16. };
  17. union {
  18. struct {
  19. unsigned long private; /* 映射时的私有非透明数据:
  20. * 如果设置PagePrivate,则用作buffer_heads;
  21. * 如果设置PageSwapCache,则用作swp_entry_t;
  22. * 如果设置PG_buddy,则表示在伙伴系统中的顺序编号
  23. */
  24. struct address_space *mapping; /* 如果低端bit清除,则指向inode地址空间,或者为null.
  25. * 如果页面被映射为匿名内存,低端bit设置,则指向
  26. * anon_vma对象,参看PAGE_MAPPING_ANON
  27. */
  28. };
  29. #if USE_SPLIT_PTLOCKS
  30. spinlock_t ptl;
  31. #endif
  32. struct kmem_cache *slab; /* SLUB: 指向slab的指针 */
  33. /* 如果属于伙伴系统,并且不是伙伴系统中的第一个页则指向第一个页 */
  34. struct page *first_page;
  35. };
  36. union { /* 如果是文件映射,那么表示本页面在文件中的位置(偏移) */
  37. pgoff_t index; /* Our offset within mapping. */
  38. void *freelist; /* SLUB: freelist req. slab lock */
  39. };
  40. struct list_head lru; /* Pageout list, eg. active_list
  41. * protected by zone->lru_lock !
  42. */
  43. /*
  44. * On machines where all RAM is mapped into kernel address space,
  45. * we can simply calculate the virtual address. On machines with
  46. * highmem some memory is mapped into kernel virtual memory
  47. * dynamically, so we need a place to store that address.
  48. * Note that this field could be 16 bits on x86 ... ;)
  49. *
  50. * Architectures with slow multiplication can define
  51. * WANT_PAGE_VIRTUAL in asm/page.h
  52. */
  53. #if defined(WANT_PAGE_VIRTUAL)
  54. void *virtual; /* 内核虚拟地址(如果没有被内核映射,则为NULL,例如高端内存hignmem) */
  55. #endif /* WANT_PAGE_VIRTUAL */
  56. #ifdef CONFIG_WANT_PAGE_DEBUG_FLAGS
  57. unsigned long debug_flags; /* Use atomic bitops on this */
  58. #endif
  59. #ifdef CONFIG_KMEMCHECK
  60. /* kmemcheck想跟踪一个page中的每个byte的状态,这是一个指向这种状态块的指针。
  61. * 如果没有被跟踪,则为NULL
  62. */
  63. void *shadow;
  64. #endif
  65. };
    该结构主要包含原子标志、使用计数、指向的地址空间、指向slab的指针、文件中的位置(如果是文件映射)、状态跟踪指针等。
     4、全局的mem_map数组:定义在include/linux/mmzone.h中,如下:
  1. #ifndef CONFIG_DISCONTIGMEM
  2. /* 物理页数组,对discontigmem使用pgdat->lmem_map */
  3. extern struct page *mem_map;
  4. #endif
    这个数组保存了所有的物理页page结构,它存储在ZONE_NORMAL内存区域的开头,用于跟踪所有的物理页面。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/249268?site
推荐阅读
相关标签
  

闽ICP备14008679号