linux情景分析第二章--存储管理(1)

linux混混之牢骚:

可怜的安阳的,你承担和温州一样的故事,却引不起老温的注意……悲哀啊,故乡。。。

2.1  linux内存管理基本框架

 linux中的分段分页机制分三层,页目录(PGD),中间目录(PMD),页表(PT)。PT中的表项称为页表项(PTE)。注意英文缩写,在linux程序中函数变量的名字等都会和英文缩写相关。

LINUX中的三级映射流程如图:

但是arm结构的MMU在硬件只有2级映射,所以在软件上会跳过PMD表。即:在PGD中直接放的是PT的base address。在linux软件上就是:

  1. #define PMD_SHIFT       21  
  2. #define PGDIR_SHIFT 21             //让PMD_SHIFT 和 PGDIR_SHIFT 相等就可以了。  


 

新的2.6内核和内核源码情景分析上的差别挺大的,在2.6.11版本以后,linux将软件上的3级映射变成了4级映射,在PMD后面增加了一个PUD(page upper directory). 在arm的两级映射中,跳过PMD和PUD

在2.6.39内核arch/arm/include/asm/pgtable.h中有下代码:

  1. /* 
  2.  * Hardware-wise, we have a two level page table structure, where the first 
  3.  * level has 4096 entries, and the second level has 256 entries.  Each entry 
  4.  * is one 32-bit word.  Most of the bits in the second level entry are used 
  5.  * by hardware, and there aren't any "accessed" and "dirty" bits. 
  6.  * 
  7.  * Linux on the other hand has a three level page table structure, which can 
  8.  * be wrapped to fit a two level page table structure easily - using the PGD 
  9.  * and PTE only.  However, Linux also expects one "PTE" table per page, and 
  10.  * at least a "dirty" bit. 
  11.  * 
  12.  * Therefore, we tweak the implementation slightly - we tell Linux that we 
  13.  * have 2048 entries in the first level, each of which is 8 bytes (iow, two 
  14.  * hardware pointers to the second level.)  The second level contains two 
  15.  * hardware PTE tables arranged contiguously, preceded by Linux versions 
  16.  * which contain the state information Linux needs.  We, therefore, end up 
  17.  * with 512 entries in the "PTE" level. 
  18.  * 
  19.  * This leads to the page tables having the following layout: 
  20.  * 
  21.  *    pgd             pte 
  22.  * |        | 
  23.  * +--------+ 
  24.  * |        |       +------------+ +0 
  25.  * +- - - - +       | Linux pt 0 | 
  26.  * |        |       +------------+ +1024 
  27.  * +--------+ +0    | Linux pt 1 | 
  28.  * |        |-----> +------------+ +2048 
  29.  * +- - - - + +4    |  h/w pt 0  | 
  30.  * |        |-----> +------------+ +3072 
  31.  * +--------+ +8    |  h/w pt 1  | 
  32.  * |        |       +------------+ +4096 
  33.  * 
  34.  * See L_PTE_xxx below for definitions of bits in the "Linux pt", and 
  35.  * PTE_xxx for definitions of bits appearing in the "h/w pt". 
  36.  * 
  37.  * PMD_xxx definitions refer to bits in the first level page table. 
  38.  * 
  39.  * The "dirty" bit is emulated by only granting hardware write permission 
  40.  * iff the page is marked "writable" and "dirty" in the Linux PTE.  This 
  41.  * means that a write to a clean page will cause a permission fault, and 
  42.  * the Linux MM layer will mark the page dirty via handle_pte_fault(). 
  43.  * For the hardware to notice the permission change, the TLB entry must 
  44.  * be flushed, and ptep_set_access_flags() does that for us. 
  45.  * 
  46.  * The "accessed" or "young" bit is emulated by a similar method; we only 
  47.  * allow accesses to the page if the "young" bit is set.  Accesses to the 
  48.  * page will cause a fault, and handle_pte_fault() will set the young bit 
  49.  * for us as long as the page is marked present in the corresponding Linux 
  50.  * PTE entry.  Again, ptep_set_access_flags() will ensure that the TLB is 
  51.  * up to date. 
  52.  * 
  53.  * However, when the "young" bit is cleared, we deny access to the page 
  54.  * by clearing the hardware PTE.  Currently Linux does not flush the TLB 
  55.  * for us in this case, which means the TLB will retain the transation 
  56.  * until either the TLB entry is evicted under pressure, or a context 
  57.  * switch which changes the user space mapping occurs. 
  58.  */  
  1. <p>#define PTRS_PER_PTE  512                              //PTE的个数  
  2. #define PTRS_PER_PMD  1  
  3. #define PTRS_PER_PGD  2048</p><p>#define PTE_HWTABLE_PTRS (PTRS_PER_PTE)  
  4. #define PTE_HWTABLE_OFF  (PTE_HWTABLE_PTRS * sizeof(pte_t))  
  5. #define PTE_HWTABLE_SIZE (PTRS_PER_PTE * sizeof(u32))</p><p>/*  
  6.  * PMD_SHIFT determines the size of the area a second-level page table can map  
  7.  * PGDIR_SHIFT determines what a third-level page table entry can map  
  8.  */  
  9. #define PMD_SHIFT  21  
  10. #define PGDIR_SHIFT  21 //另PMD和PDGIR相等,来跳过PMD。</p>  

/*linux将PGD为2k,每项为8个byte。(MMU中取值为前高12bit,为4k,每项4个byte,但linux为什么要这样做呢?) ,另外linux在定义pte时,定义了两个pte,一个供MMU使用,一个供linux使用,来用描述这个页。
 *根据注释中的表图,我看到有  #define PTRS_PER_PTE  512   #define PTRS_PER_PGD  2048 ,  linux将PDG定义为2K,8byte,
每个pte项为512,4byte。 他将 两个pte项进行了一下合并。为什么?为什么?

**/

在进程中,传说是可以看到4G的空间,按照linux的用户空间和内核空间划分。 其实进程可以看到3G的自己进程的空间,3G-4G的空间是内核空间,进程仅能通过系统调用进入。

 

2.2地址映射全过程

将x86的段地址,略。。。

2.3几个重要的数据结构和函数

PGD,PTE的值定义:

  1. /* 
  2.  * These are used to make use of C type-checking.. 
  3.  */  
  4. typedef struct { pteval_t pte; } pte_t;  
  5. typedef struct { unsigned long pmd; } pmd_t;  
  6. typedef struct { unsigned long pgd[2]; } pgd_t; //定义一个[2]数组,这样就和上面的介绍对应起来了,每个PGD是一个8个byte的值(即两个long型数)。  
  7. typedef struct { unsigned long pgprot; } pgprot_t;  

这几个结构体定义PTE等他们的结构, pgprot_t 是page protect的意思,最上面的图可知,在具体映射时候,需要将PTE表中的高20bit的值 + 线性地址的低 12bit的值  才是具体的物理地址。所以PTE只有高20bit是在地址映射映射时有效的,那么他的低12bit用来放一些protect的数据,如writeable,rdonly......下面是pgprot_t的一些值:

  1. #define __PAGE_NONE     __pgprot(_L_PTE_DEFAULT | L_PTE_RDONLY | L_PTE_XN)  
  2. #define __PAGE_SHARED       __pgprot(_L_PTE_DEFAULT | L_PTE_USER | L_PTE_XN)  
  3. #define __PAGE_SHARED_EXEC  __pgprot(_L_PTE_DEFAULT | L_PTE_USER)  
  4. #define __PAGE_COPY     __pgprot(_L_PTE_DEFAULT | L_PTE_USER | L_PTE_RDONLY | L_PTE_XN)  
  5. #define __PAGE_COPY_EXEC    __pgprot(_L_PTE_DEFAULT | L_PTE_USER | L_PTE_RDONLY)  
  6. #define __PAGE_READONLY     __pgprot(_L_PTE_DEFAULT | L_PTE_USER | L_PTE_RDONLY | L_PTE_XN)  
  7. #define __PAGE_READONLY_EXEC    __pgprot(_L_PTE_DEFAULT | L_PTE_USER | L_PTE_RDONLY)  


所以当我们想做一个PTE时(即生成一个page的pte),调用下列函数:

  1. #define mk_pte(page,prot)   pfn_pte(page_to_pfn(page), prot)  

这个函数即是 page_to_pfn(page)得到高20bit的值 + prot的值。 prot即为pgprot_t的结构型值,低12bit。表示页的属性。

 生成这一个pte后,我们要让这个pte生效,就要在将这个值 赋值到对应的地方去。

 set_pte(pteptr,pteval):  但是在2.6.39没有找到这个代码,过程应该差不多了,算了·····

另外,我们可以通过对PTE的低12bit设置,来让mmu判断,是否建立了映射?还是建立了映射但已经被swap出去了? 

还有其他很多的函数,和宏定义,具体可以看《深入linux内核》的内存寻址这章。

 

这里都是说的PTE中的低12bit的, 但是在2.6.39中明显多了一个linux pt, 具体做什么用还不太清楚。但是linux在arch/arm/include/asm/pgtable.h 中由个注释

  1. /* 
  2.  * "Linux" PTE definitions. 
  3.  * 
  4.  * We keep two sets of PTEs - the hardware and the linux version. 
  5.  * This allows greater flexibility in the way we map the Linux bits 
  6.  * onto the hardware tables, and allows us to have YOUNG and DIRTY 
  7.  * bits. 
  8.  * 
  9.  * The PTE table pointer refers to the hardware entries; the "Linux" 
  10.  * entries are stored 1024 bytes below. 
  11.  */  

应该可以看出,linux pt和PTE的低12bit是相呼应的,用来记录page的一些信息。

 

 

linux中,每个物理页都有一个对应的page结构体,组成一个数组,放在mem_map中。

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

页的page结构放在mem_map中。  要想找到一个物理地址对应的page结构很简单, 就是   mem_map[pfn]即可。    
 

 

由于硬件的关系,会将整个内存分成不同的zone,:ZONE_DMA, ZONE_NORMAL, ZONE_HIGHMEM. 这样分的原因是 :有些芯片的DMA设置只能设置到固定区域的地址,所以将这些区域作为ZONE_DMA以免其他操作占用。NORMAL就是正常的内存。高位内存内核空间因为只有1G不能全部映射,所以也要做特殊的处理,进行映射,才能使用。

这三个ZONE使用的描述符是:

  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];  
  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  
  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;  
  31. #endif  
  32.     struct per_cpu_pageset __percpu *pageset;  
  33.     /* 
  34.      * free areas of different sizes 
  35.      */  
  36.     spinlock_t      lock;  
  37.     int                     all_unreclaimable; /* All pages pinned */  
  38. #ifdef CONFIG_MEMORY_HOTPLUG  
  39.     /* see spanned/present_pages for more description */  
  40.     seqlock_t       span_seqlock;  
  41. #endif  
  42.     struct free_area    free_area[MAX_ORDER];  
  43.   
  44. #ifndef CONFIG_SPARSEMEM  
  45.     /* 
  46.      * Flags for a pageblock_nr_pages block. See pageblock-flags.h. 
  47.      * In SPARSEMEM, this map is stored in struct mem_section 
  48.      */  
  49.     unsigned long       *pageblock_flags;  
  50. #endif /* CONFIG_SPARSEMEM */  
  51.   
  52. #ifdef CONFIG_COMPACTION  
  53.     /* 
  54.      * On compaction failure, 1<<compact_defer_shift compactions 
  55.      * are skipped before trying again. The number attempted since 
  56.      * last failure is tracked with compact_considered. 
  57.      */  
  58.     unsigned int        compact_considered;  
  59.     unsigned int        compact_defer_shift;  
  60. #endif  
  61.   
  62.     ZONE_PADDING(_pad1_)  
  63.   
  64.     /* Fields commonly accessed by the page reclaim scanner */  
  65.     spinlock_t      lru_lock;     
  66.     struct zone_lru {  
  67.         struct list_head list;  
  68.     } lru[NR_LRU_LISTS];  
  69.   
  70.     struct zone_reclaim_stat reclaim_stat;  
  71.   
  72.     unsigned long       pages_scanned;     /* since last reclaim */  
  73.     unsigned long       flags;         /* zone flags, see below */  
  74.   
  75.     /* Zone statistics */  
  76.     atomic_long_t       vm_stat[NR_VM_ZONE_STAT_ITEMS];  
  77.   
  78.     /* 
  79.      * The target ratio of ACTIVE_ANON to INACTIVE_ANON pages on 
  80.      * this zone's LRU.  Maintained by the pageout code. 
  81.      */  
  82.     unsigned int inactive_ratio;  
  83.   
  84.   
  85.     ZONE_PADDING(_pad2_)  
  86.     /* Rarely used or read-mostly fields */  
  87.   
  88.     /* 
  89.      * wait_table       -- the array holding the hash table 
  90.      * wait_table_hash_nr_entries   -- the size of the hash table array 
  91.      * wait_table_bits  -- wait_table_size == (1 << wait_table_bits) 
  92.      * 
  93.      * The purpose of all these is to keep track of the people 
  94.      * waiting for a page to become available and make them 
  95.      * runnable again when possible. The trouble is that this 
  96.      * consumes a lot of space, especially when so few things 
  97.      * wait on pages at a given time. So instead of using 
  98.      * per-page waitqueues, we use a waitqueue hash table. 
  99.      * 
  100.      * The bucket discipline is to sleep on the same queue when 
  101.      * colliding and wake all in that wait queue when removing. 
  102.      * When something wakes, it must check to be sure its page is 
  103.      * truly available, a la thundering herd. The cost of a 
  104.      * collision is great, but given the expected load of the 
  105.      * table, they should be so rare as to be outweighed by the 
  106.      * benefits from the saved space. 
  107.      * 
  108.      * __wait_on_page_locked() and unlock_page() in mm/filemap.c, are the 
  109.      * primary users of these fields, and in mm/page_alloc.c 
  110.      * free_area_init_core() performs the initialization of them. 
  111.      */  
  112.     wait_queue_head_t   * wait_table;  
  113.     unsigned long       wait_table_hash_nr_entries;  
  114.     unsigned long       wait_table_bits;  
  115.   
  116.     /* 
  117.      * Discontig memory support fields. 
  118.      */  
  119.     struct pglist_data  *zone_pgdat;  
  120.     /* zone_start_pfn == zone_start_paddr >> PAGE_SHIFT */  
  121.     unsigned long       zone_start_pfn;  
  122.   
  123.     /* 
  124.      * zone_start_pfn, spanned_pages and present_pages are all 
  125.      * protected by span_seqlock.  It is a seqlock because it has 
  126.      * to be read outside of zone->lock, and it is done in the main 
  127.      * allocator path.  But, it is written quite infrequently. 
  128.      * 
  129.      * The lock is declared along with zone->lock because it is 
  130.      * frequently read in proximity to zone->lock.  It's good to 
  131.      * give them a chance of being in the same cacheline. 
  132.      */  
  133.     unsigned long       spanned_pages;  /* total size, including holes */  
  134.     unsigned long       present_pages;  /* amount of memory (excluding holes) */  
  135.   
  136.     /* 
  137.      * rarely used fields: 
  138.      */  
  139.     const char      *name;  
  140. } ____cacheline_internodealigned_in_smp;  


在这个描述符中,有一个struct free_area free_area[MAX_ORDER];  这样的成员变量,这个数组的每个元素都包含一个page的list,那么,一共有MAX_ORDER个list。他是将 所有的free page按照连续是否进行分组。 有2个连续的page的,有4个连续的page的,有8个连续的page的,....2^MAX_ORDER。  这样将page进行分类。当需要alloc一些page时,可以方便的从这些list中找连续的page。(slub中也会像这样来分类,但是slub中分类是按byte为单位,如2byte,4byte....)

 

另外还有一个 

  1. struct zone_lru {  
  2.         struct list_head list;  
  3.     } lru[NR_LRU_LISTS];  
  1. <p>/* 
  2.  * We do arithmetic on the LRU lists in various places in the code, 
  3.  * so it is important to keep the active lists LRU_ACTIVE higher in 
  4.  * the array than the corresponding inactive lists, and to keep 
  5.  * the *_FILE lists LRU_FILE higher than the corresponding _ANON lists. 
  6.  * 
  7.  * This has to be kept in sync with the statistics in zone_stat_item 
  8.  * above and the descriptions in vmstat_text in mm/vmstat.c 
  9.  */  
  10. #define LRU_BASE 0  
  11. #define LRU_ACTIVE 1  
  12. #define LRU_FILE 2</p><p>enum lru_list {  
  13.  LRU_INACTIVE_ANON = LRU_BASE,  
  14.  LRU_ACTIVE_ANON = LRU_BASE + LRU_ACTIVE,  
  15.  LRU_INACTIVE_FILE = LRU_BASE + LRU_FILE,  
  16.  LRU_ACTIVE_FILE = LRU_BASE + LRU_FILE + LRU_ACTIVE,  
  17.  LRU_UNEVICTABLE,  
  18.  NR_LRU_LISTS  
  19. };</p>  

这样的成员变量,这也是一个list数组, 其中NR_LRU_LISTS表示在enum 中定义的成员的个数(新的linux中好像有不少这样的用法),这个list数组中,每个list也是很多page结构体。LRU是一中算法,会将长时间不同的page给swap out到flash中。这个数组是LRU算法用的。 其中有 inactive的page, active的page, inactive 的file,等  在后面对LRU介绍。

 

UMA和NUMA

在linux中,如果CPU访问所有的memory所需的时间都是一样的,那么我们人为这个系统是UMA(uniform memory architecture),但是如果访问memory所需的时间是不一样的(在SMP(多核系统)是很常见的),那么我们人为这个系统是NUMA(Non-uniform memory architecture)。在linux中,系统会将内存分成几个node,每个node中的memory,CPU进入的时间的相等的。这个我们分配内存的时候就会从一个node进行分配。

  1. typedef struct pglist_data {  
  2.     struct zone node_zones[MAX_NR_ZONES];  
  3.     struct zonelist node_zonelists[MAX_ZONELISTS];  
  4.     int nr_zones;  
  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. #ifndef CONFIG_NO_BOOTMEM  
  12.     struct bootmem_data *bdata;  
  13. #endif  
  14. #ifdef CONFIG_MEMORY_HOTPLUG  
  15.     /* 
  16.      * Must be held any time you expect node_start_pfn, node_present_pages 
  17.      * or node_spanned_pages stay constant.  Holding this will also 
  18.      * guarantee that any pfn_valid() stays that way. 
  19.      * 
  20.      * Nests above zone->lock and zone->size_seqlock. 
  21.      */  
  22.     spinlock_t node_size_lock;  
  23. #endif  
  24.     unsigned long node_start_pfn;  
  25.     unsigned long node_present_pages; /* total number of physical pages */  
  26.     unsigned long node_spanned_pages; /* total size of physical page 
  27.                          range, including holes */  
  28.     int node_id;  
  29.     wait_queue_head_t kswapd_wait;  
  30.     struct task_struct *kswapd;  
  31.     int kswapd_max_order;  
  32.     enum zone_type classzone_idx;  
  33. } pg_data_t;  

在pglist_data结构中,一个成员变量node_zones[], 他代表了这个node下的所有zone。组成一个数组。

另一个成员变量node_zonelist[]. 这是一个list数组,每一个list中将不同node下的所有的zone都link到一起。数组中不同元素list中,zone的排列顺序不一样。这样当内核需要malloc一些page的时候,可能当前的这个node中并没有足够的page,那么就会按照这个数组list中的顺序依次去申请空间。内核在不同的情况下,需要按照不同的顺序申请空间。所以需要好几个不同的list,这些list就组成了这个数组。

每一个pglist_data结构对应一个node。这样,在每个zone结构上又多了一个node结构。这样内存页管理的结构应该是

node:同过UMA和NUMA将内存分成几个node。(在arm系统中,如果不是smp的一般都是一个node)

zone:在每个node中,再将内存分配成几个zone。

page:在每个zone中,对page进行管理,添加都各种list中。

for example: 可以分析一下 for_each_zone这个宏定义,会发现就是先查找每个node,在每个node下进行zone的扫描。

 

上面的这些都是描述物理空间的,page,zone,node都是物理空间的管理结构,下面的结构体,描述虚拟空间

从物理空间来看,物理空间主要是“供”,他是实实在在存在的,主要目的就是向OS提供空间。 

从虚拟空间来看,虚拟空间是“需”,他是虚拟的,主要是就发送需求。

*****当虚拟空间提出了 “需求”,但物理空间无法满足时候,就会进行swap out操作了。

 

vm_area_struct结构:这个结构描述  进程的的内存空间的情况。

  1. /* 
  2.  * This struct defines a memory VMM memory area. There is one of these 
  3.  * per VM-area/task.  A VM area is any part of the process virtual memory 
  4.  * space that has a special rule for the page-fault handlers (ie a shared 
  5.  * library, the executable area etc). 
  6.  */  
  7. struct vm_area_struct {  
  8.     struct mm_struct * vm_mm;   /* The address space we belong to. */  
  9.     unsigned long vm_start;     /* Our start address within vm_mm. */  
  10.     unsigned long vm_end;       /* The first byte after our end address 
  11.                        within vm_mm. */  
  12.   
  13.     /* linked list of VM areas per task, sorted by address */  
  14.     struct vm_area_struct *vm_next, *vm_prev;  
  15.   
  16.     pgprot_t vm_page_prot;      /* Access permissions of this VMA. */  
  17.     unsigned long vm_flags;     /* Flags, see mm.h. */  
  18.   
  19.     struct rb_node vm_rb;  
  20.   
  21.     /* 
  22.      * For areas with an address space and backing store, 
  23.      * linkage into the address_space->i_mmap prio tree, or 
  24.      * linkage to the list of like vmas hanging off its node, or 
  25.      * linkage of vma in the address_space->i_mmap_nonlinear list. 
  26.      */  
  27.     union {  
  28.         struct {  
  29.             struct list_head list;  
  30.             void *parent;   /* aligns with prio_tree_node parent */  
  31.             struct vm_area_struct *head;  
  32.         } vm_set;  
  33.   
  34.         struct raw_prio_tree_node prio_tree_node;  
  35.     } shared;  
  36.   
  37.     /* 
  38.      * A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma 
  39.      * list, after a COW of one of the file pages.  A MAP_SHARED vma 
  40.      * can only be in the i_mmap tree.  An anonymous MAP_PRIVATE, stack 
  41.      * or brk vma (with NULL file) can only be in an anon_vma list. 
  42.      */  
  43.     struct list_head anon_vma_chain; /* Serialized by mmap_sem & 
  44.                       * page_table_lock */  
  45.     struct anon_vma *anon_vma;  /* Serialized by page_table_lock */  
  46.   
  47.     /* Function pointers to deal with this struct. */  
  48.     const struct vm_operations_struct *vm_ops;  
  49.   
  50.     /* Information about our backing store: */  
  51.     unsigned long vm_pgoff;     /* Offset (within vm_file) in PAGE_SIZE 
  52.                        units, *not* PAGE_CACHE_SIZE */  
  53.     struct file * vm_file;      /* File we map to (can be NULL). */  
  54.     void * vm_private_data;     /* was vm_pte (shared mem) */  
  55.     unsigned long vm_truncate_count;/* truncate_count or restart_addr */  
  56.   
  57. #ifndef CONFIG_MMU  
  58.     struct vm_region *vm_region;    /* NOMMU mapping region */  
  59. #endif  
  60. #ifdef CONFIG_NUMA  
  61.     struct mempolicy *vm_policy;    /* NUMA policy for the VMA */  
  62. #endif  
  63. };  

这个数据结构在程序的变量名字常常是vma。

这个结构是成员变量,  vm_start 和vm_end表示一段连续的虚拟区间。  但是并不是一个连续的虚拟区间就可以用一个vm_area_struct结构来表示。而是要 虚拟地址连续,并且这段空间的属性/访问权限等也相同才可以。

所以这段空间的属于和权限用

  1. pgprot_t vm_page_prot;  /* Access permissions of this VMA. */  
  2.  unsigned long vm_flags;  /* Flags, see mm.h. */   

这两个成员变量来表示。看到这两个成员变量可算看到亲人了。 还记得那个大明湖畔的容嬷嬷妈?-------pgprot_t vm_page_prot;

每一个进程的所有vm_erea_struct结构通过

  1. /* linked list of VM areas per task, sorted by address */  
  2.     struct vm_area_struct *vm_next, *vm_prev;  

这个指针link起来。

当然,有上面这种链表的链接方式,查找起来是很麻烦的,而且一个进程中可能会有好多个这样的结构,所以 除了上面的链表,还要有一中更有效的查找方式:

  1. <span style="font-size:13px;">  /* 
  2.      * For areas with an address space and backing store, 
  3.      * linkage into the address_space->i_mmap prio tree, or 
  4.      * linkage to the list of like vmas hanging off its node, or 
  5.      * linkage of vma in the address_space->i_mmap_nonlinear list. 
  6.      */  
  7.     union {  
  8.         struct {  
  9.             struct list_head list;  
  10.             void *parent;   /* aligns with prio_tree_node parent */  
  11.             struct vm_area_struct *head;  
  12.         } vm_set;  
  13.   
  14.         struct raw_prio_tree_node prio_tree_node;  
  15.     } shared;  
  16. </span>  


 

有两种情况虚拟空间会与磁盘flash发生关系。

1. 当内存分配失败,没有足够的内存时候,会发生swap。

2. linux进行mmap系统时候,会将磁盘上的内存map到用户空间,像访问memory一样,直接访问文件内容。 

 

为了迎合1. vm_area_struct 的mapping,vm_next_share,vm_pprev_share,vm_file等。但是在2.6.39中没找到这些变量。仅有:(在page结构体中,也有swap相关的信息)

  1. /* Information about our backing store: */  
  2.     unsigned long vm_pgoff;     /* Offset (within vm_file) in PAGE_SIZE 
  3.                        units, *not* PAGE_CACHE_SIZE */  
  4.     struct file * vm_file;      /* File we map to (can be NULL). */  
  5.     void * vm_private_data;     /* was vm_pte (shared mem) */  
  6.     unsigned long vm_truncate_count;/* truncate_count or restart_addr */  

在后面再分析这些结构。

为了迎合2. vm_area_struct结构提供了

  1. /* Function pointers to deal with this struct. */  
  2.     const struct vm_operations_struct *vm_ops;  

这样的变量,进行操作。

 

在vm_area_struct结构中,有个mm_struct结构,注释说他属于这个结构,由此看来,mm_struct结构应该是vm_area_struct结构的上层。

  1. struct mm_struct {  
  2.     struct vm_area_struct * mmap;       /* list of VMAs */  
  3.     struct rb_root mm_rb;  
  4.     struct vm_area_struct * mmap_cache; /* last find_vma result */  
  5. #ifdef CONFIG_MMU  
  6.     unsigned long (*get_unmapped_area) (struct file *filp,  
  7.                 unsigned long addr, unsigned long len,  
  8.                 unsigned long pgoff, unsigned long flags);  
  9.     void (*unmap_area) (struct mm_struct *mm, unsigned long addr);  
  10. #endif  
  11.     unsigned long mmap_base;        /* base of mmap area */  
  12.     unsigned long task_size;        /* size of task vm space */  
  13.     unsigned long cached_hole_size;     /* if non-zero, the largest hole below free_area_cache */  
  14.     unsigned long free_area_cache;      /* first hole of size cached_hole_size or larger */  
  15.     pgd_t * pgd;  
  16.     atomic_t mm_users;          /* How many users with user space? */  
  17.     atomic_t mm_count;          /* How many references to "struct mm_struct" (users count as 1) */  
  18.     int map_count;              /* number of VMAs */  
  19.   
  20.     spinlock_t page_table_lock;     /* Protects page tables and some counters */  
  21.     struct rw_semaphore mmap_sem;  
  22.   
  23.     struct list_head mmlist;        /* List of maybe swapped mm's.  These are globally strung 
  24.                          * together off init_mm.mmlist, and are protected 
  25.                          * by mmlist_lock 
  26.                          */  
  27.   
  28.   
  29.     unsigned long hiwater_rss;  /* High-watermark of RSS usage */  
  30.     unsigned long hiwater_vm;   /* High-water virtual memory usage */  
  31.   
  32.     unsigned long total_vm, locked_vm, shared_vm, exec_vm;  
  33.     unsigned long stack_vm, reserved_vm, def_flags, nr_ptes;  
  34.     unsigned long start_code, end_code, start_data, end_data;  
  35.     unsigned long start_brk, brk, start_stack;  
  36.     unsigned long arg_start, arg_end, env_start, env_end;  
  37.   
  38.     unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */  
  39.   
  40.     /* 
  41.      * Special counters, in some configurations protected by the 
  42.      * page_table_lock, in other configurations by being atomic. 
  43.      */  
  44.     struct mm_rss_stat rss_stat;  
  45.   
  46.     struct linux_binfmt *binfmt;  
  47.   
  48.     cpumask_t cpu_vm_mask;  
  49.   
  50.     /* Architecture-specific MM context */  
  51.     mm_context_t context;  
  52.   
  53.     /* Swap token stuff */  
  54.     /* 
  55.      * Last value of global fault stamp as seen by this process. 
  56.      * In other words, this value gives an indication of how long 
  57.      * it has been since this task got the token. 
  58.      * Look at mm/thrash.c 
  59.      */  
  60.     unsigned int faultstamp;  
  61.     unsigned int token_priority;  
  62.     unsigned int last_interval;  
  63.   
  64.     /* How many tasks sharing this mm are OOM_DISABLE */  
  65.     atomic_t oom_disable_count;  
  66.   
  67.     unsigned long flags; /* Must use atomic bitops to access the bits */  
  68.   
  69.     struct core_state *core_state; /* coredumping support */  
  70. #ifdef CONFIG_AIO  
  71.     spinlock_t      ioctx_lock;  
  72.     struct hlist_head   ioctx_list;  
  73. #endif  
  74. #ifdef CONFIG_MM_OWNER  
  75.     /* 
  76.      * "owner" points to a task that is regarded as the canonical 
  77.      * user/owner of this mm. All of the following must be true in 
  78.      * order for it to be changed: 
  79.      * 
  80.      * current == mm->owner 
  81.      * current->mm != mm 
  82.      * new_owner->mm == mm 
  83.      * new_owner->alloc_lock is held 
  84.      */  
  85.     struct task_struct __rcu *owner;  
  86. #endif  
  87.   
  88. #ifdef CONFIG_PROC_FS  
  89.     /* store ref to file /proc/<pid>/exe symlink points to */  
  90.     struct file *exe_file;  
  91.     unsigned long num_exe_file_vmas;  
  92. #endif  
  93. #ifdef CONFIG_MMU_NOTIFIER  
  94.     struct mmu_notifier_mm *mmu_notifier_mm;  
  95. #endif  
  96. #ifdef CONFIG_TRANSPARENT_HUGEPAGE  
  97.     pgtable_t pmd_huge_pte; /* protected by page_table_lock */  
  98. #endif  
  99. };  


这个结构在代码中,经常是mm的名字。

他比vm_area_struct结构更上层,每个进程只有一个mm_struct结构在task_struct中由指针。因此mm_struct结构会更具总结型。

所以虚拟空间的联系图为:

 

 

总结一下:

讲解了,

1.PGD,PTE,

2.node--->zone---->page

3. mm_struct------>vm_area_struct

 

虚拟地址和物理地址 通过 PGD,PTE联系起来。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值