- 论坛徽章:
- 0
|
本帖最后由 71v5 于 2014-07-17 19:14 编辑
内存系统在SI_SUB_VM对应的subsystem中初始化,可以看出内存系统的初始化函数被赋予较高的优先级:- SI_SUB_VM = 0x1000000, /* virtual memory system init*/
复制代码 freebsd9.2中和内存相关的初始化函数如下:- 100: SYSINIT(vm_mem, SI_SUB_VM, SI_ORDER_FIRST, vm_mem_init, NULL);
- [the above rsults in file /usr/src/sys/vm/vm_init.c]
- 145: SYSINIT(vm_page, SI_SUB_VM, SI_ORDER_SECOND, vm_page_init_fakepg, NULL);
- [the above rsults in file /usr/src/sys/vm/vm_page.c]
- 255: SYSINIT(uma_startup3, SI_SUB_VM_CONF, SI_ORDER_SECOND, uma_startup3, NULL);
- [the above rsults in file /usr/src/sys/vm/uma_core.c]
- 上面的初始化函数vm_page_init_fakepg和uma_startup3都和UMA相关,我们暂且不做分析。
复制代码 下面主要看看初始化初始化函数vm_mem_init:- /**********************************************************************************
- * 函数vm_mem_init将依次调用7个初始化函数,这7个函数执行完后,内存系统就可以基本
- 上正常工作了。
- 函数vm_set_page_size比较简单,请见下面的描述。
- 我们先简要分析初始化函数vm_page_startup,后面5个初始化函数先简略描述一下,后续
- 再进行分析。
- 函数vm_object_init:
- 1:初始化两个静态定义的struct vm_object对象:
- struct vm_object kernel_object_store;
- struct vm_object kmem_object_store;
- 2:创建用来分配struct vm_object对象的UMA区域,并且由下面的变量来标识这个UMA区域:
- static uma_zone_t obj_zone;
- 函数vm_map_startup:
- 1:创建用来分配struct vm_map对象的UMA区域.
- 2:创建用来给内核分配struct vm_map_entry对象的UMA区域.
- kmapentzone指向这个UMA区域。
- 3:创建给普通请求者分配struct vm_map_entry对象的UMA区域.
- mapentzone指向这个UMA区域。
- 函数kmem_init:
- 创建数据结构来描述内核的地址空间。
-
- 函数pmap_init:
- 1:Initialize the pmap module。
- 2:初始化描述保存内核页表的物理内存的页框描述符等。
- 函数vm_pager_init:
- 完成调页器接口的初始化,调页器接口提供了在后备存储和物理内存之间转移
- 数据的机制。
- ************************************/
- 102 /*
- 103 * vm_init initializes the virtual memory system.
- 104 * This is done only by the first cpu up.
- 105 *
- 106 * The start and end address of physical memory is passed in.
- 107 */
- 108 /* ARGSUSED*/
- 109 static void
- 110 vm_mem_init(dummy)
- 111 void *dummy;
- 112 {
- 113 /*
- 114 * Initializes resident memory structures. From here on, all physical
- 115 * memory is accounted for, and we use only virtual addresses.
- 116 */
- 117 vm_set_page_size();
- 118 virtual_avail = vm_page_startup(virtual_avail);
- 119
- 120 /*
- 121 * Initialize other VM packages
- 122 */
- 123 vm_object_init();
- 124 vm_map_startup();
- 125 kmem_init(virtual_avail, virtual_end);
- 126 pmap_init();
- 127 vm_pager_init();
- 128 }
复制代码 [函数vm_set_page_size]:- /**************************************************************************
- * vm_set_page_size:
- *
- * Sets the page size, perhaps based upon the memory
- * size. Must be called before any use of page-size
- * dependent functions.
-
- struct vmmeter cnt;是一个系统范围内的计数器。
- u_int v_page_size; page size in bytes
- ************************************************/
- 197 void
- 198 vm_set_page_size(void)
- 199 {
- 200 if (cnt.v_page_size == 0)
- 201 cnt.v_page_size = PAGE_SIZE;
- 202 if (((cnt.v_page_size - 1) & cnt.v_page_size) != 0)
- 203 panic("vm_set_page_size: page size not a power of two");
- 204 }
复制代码 在分析初始化函数vm_page_startup之前,先来看看要用到的部分数据结构等,暂时只需要明白这些数据结构及其
成员的大概含义就行,后续在分析相关内核函数的实现时再回头看看这些数据结构及其成员到底如何使用。
[物理页框描述符--struct vm_page]-在freebsd9.2中,对于每一个物理页框,都有一个struct vm_page类型的数据对象来描述这个物理页框,该
结构体是虚拟内存系统使用物理内存的最底层数据结构:- /******************************************************************************
- * typedef struct vm_page *vm_page_t;
- 成员大概含义可以参考每个成员的注释。现在只需要明白对于一个物理页框,都有
- 一个类型为struct vm_page类型的数据对象来描述它。
- phys_addr:相应物理页框的物理地址。
- md:用来管理映射到该物理页框的全部虚拟线性地址。
- queue:在数组vm_page_queues中的索引。
- segind:在数组vm_phys_segs中的索引,即相应的物理页框在数组元素
- vm_phys_segs[segind]描述的物理内存段内。
- pool:三维数组vm_phys_free_queues第二维的索引。
- order:数组vm_phys_free_queues第三维的索引,比如链表
- vm_phys_free_queues[VM_FREELIST_DEFAULT][VM_FREEPOOL_DEFAULT][order]
- 中链接的内存块大小为2^order个物理页框。
- ***********************/
- 130 struct vm_page {
- 131 TAILQ_ENTRY(vm_page) pageq; /* queue info for FIFO queue or free list (Q) */
- 132 TAILQ_ENTRY(vm_page) listq; /* pages in same object (O) */
- 133 struct vm_page *left; /* splay tree link (O) */
- 134 struct vm_page *right; /* splay tree link (O) */
- 135
- 136 vm_object_t object; /* which object am I in (O,P)*/
- 137 vm_pindex_t pindex; /* offset into object (O,P) */
- 138 vm_paddr_t phys_addr; /* physical address of page */
- 139 struct md_page md; /* machine dependant stuff */
- 140 uint8_t queue; /* page queue index (P,Q) */
- 141 int8_t segind;
- 142 short hold_count; /* page hold count (P) */
- 143 uint8_t order; /* index of the buddy queue */
- 144 uint8_t pool;
- 145 u_short cow; /* page cow mapping count (P) */
- 146 u_int wire_count; /* wired down maps refs (P) */
- 147 uint8_t aflags; /* access is atomic */
- 148 uint8_t flags; /* see below, often immutable after alloc */
- 149 u_short oflags; /* page flags (O) */
- 150 u_char act_count; /* page usage count (O) */
- 151 u_char busy; /* page busy count (O) */
- 152 /* NOTE that these must support one bit per DEV_BSIZE in a page!!! */
- 153 /* so, on normal X86 kernels, they must be at least 8 bits wide */
- 154 vm_page_bits_t valid; /* map of valid DEV_BSIZE chunks (O) */
- 155 vm_page_bits_t dirty; /* map of dirty DEV_BSIZE chunks (M) */
- 156 };
复制代码 [相关全局变量]:- /********************************************************************************
- * typedef struct vm_page *vm_page_t;
- vm_page_array:类型为struct vm_page的数组,数组元素用来描述相应的物理页框。
- vm_page_array_size:数组struct vm_page中元素的数目。
- first_page:系统中可以使用的第一个物理页框号。
- ****************************************/
- 124 vm_page_t vm_page_array;
- 125 long vm_page_array_size;
- 126 long first_page;
复制代码 [管理相应状态物理页框的队列--struct vpgqueues]:- /**********************************************************************
- * TAILQ_HEAD(pglist, vm_page); 展开宏TAILQ_HEAD如下所示:
- struct pglist {
- struct vm_page *tqh_first;
- struct vm_page **tqh_last;
- TRACEBUF
- };
-
- pl:链接了相应状态的物理页框。
- cnt:指向的对象记录了相应状态物理页框的数目。
- *****************************************/
- 184 struct vpgqueues {
- 185 struct pglist pl;
- 186 int *cnt;
- 187 };
复制代码 [数组vm_page_queues]:- /*********************************************************************
- * #define PQ_COUNT 3
- #define PQ_NONE 255
- 数组vm_page_queues的索引如下:
- #define PQ_INACTIVE 0
- #define PQ_ACTIVE 1
- #define PQ_HOLD 2
- vm_page_queues[PQ_INACTIVE]中的物理页框处于非活动状态。
- vm_page_queues[PQ_ACTIVE]中的物理页框处于活动状态。
- vm_page_queues[PQ_HOLD]中的物理页框当前的引用计数器非零。
- **************************************************/
- struct vpgqueues vm_page_queues[PQ_COUNT];
复制代码 [链接空闲物理页框--struct vm_freelist]:- /**********************************************************************
- * TAILQ_HEAD(pglist, vm_page); 展开宏TAILQ_HEAD如下所示:
- struct pglist {
- struct vm_page *tqh_first;
- struct vm_page **tqh_last;
- TRACEBUF
- };
-
- pl:链接了空闲物理页框。
- cnt:相应内存块的数目。
- *****************************************/
- 67 struct vm_freelist {
- 68 struct pglist pl;
- 69 int lcnt;
- 70 };
复制代码 [管理空闲物理页框的队列--三维数组vm_phys_free_queues]:- /****************************************************************************************************************
- * #define VM_NDOMAIN 1
- #define VM_RAW_NFREELIST (VM_NFREELIST + VM_NDOMAIN - 1)
- 第一维索引可以取值如下:
- #define VM_NFREELIST 2
- #define VM_FREELIST_DEFAULT 0
- #define VM_FREELIST_ISADMA 1
- 第一维描述:
- Create two free page lists: VM_FREELIST_DEFAULT is for physical pages that are above the largest
- physical address that is accessible by ISA DMA and VM_FREELIST_ISADMA is for physical pages that
- are below that address.
- 从这里可以看出,创建了两个空闲物理页框队列:
- 队列1:二维数组vm_phys_free_queues[VM_FREELIST_ISADMA],链接了ISADMA使用的空闲物理页框。
- 队列2:二维数组vm_phys_free_queues[VM_FREELIST_DEFAULT],链接了非ISADMA使用的空闲物理页框。
-
- 第二维索引可以取值如下:
- #define VM_NFREEPOOL 2
- #define VM_FREEPOOL_CACHE 1
- #define VM_FREEPOOL_DEFAULT 0
- #define VM_FREEPOOL_DIRECT 0
- 第二维描述:
- Create two free page pools. Since the i386 kernel virtual address space does not include a mapping
- onto the machine's entire physical memory, VM_FREEPOOL_DIRECT is defined as an alias for the default
- pool, VM_FREEPOOL_DEFAULT.
- 从这里可以看出,在上面每个空闲队列里,创建了两个空闲物理页框池,总共4个物理页框池:
- 页框池1:一维数组vm_phys_free_queues[VM_FREELIST_ISADMA][VM_FREEPOOL_DEFAULT]。
- 页框池2:一维数组vm_phys_free_queues[VM_FREELIST_ISADMA][VM_FREEPOOL_CACHE]。
- 页框池3:一维数组vm_phys_free_queues[VM_FREELIST_DEFAULT][VM_FREEPOOL_DEFAULT]。
- 页框池4:一维数组vm_phys_free_queues[VM_FREELIST_DEFAULT][VM_FREEPOOL_CACHE]。
- 页框池2和页框池4中的物理页框对应的页框描述符都设置了PG_CACHED标志。
- 第三维索引可以取值如下:
- #ifdef PAE
- #define VM_NFREEORDER 10
- #else
- #define VM_NFREEORDER 11
- #endif
- 第三维描述:
- The largest allocation size is 2MB under PAE and 4MB otherwise。
- 这里形象的描述一下,对于物理页框池vm_phys_free_queues[VM_FREELIST_DEFAULT][VM_FREEPOOL_DEFAULT]:
- vm_phys_free_queues[VM_FREELIST_DEFAULT][VM_FREEPOOL_DEFAULT][0]中链接的内存块大小为2^0个物理页框(4KB)。
- vm_phys_free_queues[VM_FREELIST_DEFAULT][VM_FREEPOOL_DEFAULT][1]中链接的内存块大小为2^1个物理页框(8KB)。
- vm_phys_free_queues[VM_FREELIST_DEFAULT][VM_FREEPOOL_DEFAULT][2]中链接的内存块大小为2^2个物理页框(16KB)。
- vm_phys_free_queues[VM_FREELIST_DEFAULT][VM_FREEPOOL_DEFAULT][3]中链接的内存块大小为2^3个物理页框(32KB)。
- .....................................................
- vm_phys_free_queues[VM_FREELIST_DEFAULT][VM_FREEPOOL_DEFAULT][10]中链接的内存块大小为2^10个物理页框(4MB)。
- 可见,对于没有启用PAE的系统,能请求的最大内存块为4MB;启用PAE的系统,能请求的最大内存块为2MB。
- ***************************************************************************/
- 95 static struct vm_freelist vm_phys_free_queues[VM_RAW_NFREELIST][VM_NFREEPOOL][VM_NFREEORDER];
复制代码 [指针数组vm_phys_lookup_lists]:- /***********************************************************************************************************
- * #define VM_RAW_NFREELIST (VM_NFREELIST + VM_NDOMAIN - 1)
- 从这个指针数组的定义来看,物理内存的组织应该是存储器域->空闲物理页框链表->空闲物理页框池->空闲物理页框。
- 为了降低复杂性,这里假设只有一个存储器域,VM_NDOMAIN的值为1。
- 在i386平台,freebsd9.2默认只有一个存储器域。
- Only one memory domain.
- #ifndef VM_NDOMAIN
- #define VM_NDOMAIN 1
- #endif
- ***************************************************/
- static struct vm_freelist (*vm_phys_lookup_lists[VM_NDOMAIN][VM_RAW_NFREELIST])[VM_NFREEPOOL][VM_NFREEORDER];
复制代码 [描述物理内存区--struct vm_phys_seg]:- /*******************************************************************************************
- * #define VM_PHYSSEG_MAX 17
- static struct vm_phys_seg vm_phys_segs[VM_PHYSSEG_MAX]
- static int vm_phys_nsegs;
- 之前可用物理内存区由数组phys_avail来描述,初始化完成后,将由数组vm_phys_segs来描述。
- start:
- 相应物理内存段的起始物理地址。
- end:
- 相应物理内存段的结尾物理地址。
- domain:
- 所属的存储器域。
- first_page:
- 可以将该成员看成为struct vm_page类型的数组,每个数组元素用来描述该物理内存段一个
- 物理页框:
- first_page[0]用来描述该物理内存段内第一个物理页框。
- first_page[1]用来描述该物理内存段内第二个物理页框。
- first_page[2]用来描述该物理内存段内第三个物理页框。
- ...................................................
-
- free_queues:
- 指向上面两个空闲物理页框队列中的一个。
- ***********************************************************/
- 72 struct vm_phys_seg {
- 73 vm_paddr_t start;
- 74 vm_paddr_t end;
- 75 vm_page_t first_page;
- 76 int domain;
- 77 struct vm_freelist (*free_queues)[VM_NFREEPOOL][VM_NFREEORDER];
- 78 };
复制代码 |
|