- 论坛徽章:
- 0
|
再来看看数组vm_phys_segs:- /************************************************************************************************
- * #define VM_PHYSSEG_MAX 17
- static struct vm_phys_seg vm_phys_segs[VM_PHYSSEG_MAX]
- static int vm_phys_nsegs;
- 函数vm_phys_init的主要工作是,对于由数组phys_avail描述的每一个物理内存区域,为其分配
- 一个类型为struct vm_phys_seg的数据对象,并做适当的初始化,该数据对象包含了关于这个物理内存
- 区域的更多附加信息。
- 变量vm_phys_nsegs在下面的_vm_phys_create_seg函数中使用,作为数组vm_phys_segs的索引.
-
-
- 成员描述:
- start:
- 相应物理内存段的起始物理地址。
- end:
- 相应物理内存段的结尾物理地址。
- domain:
- 所属的存储器域。
- first_page:
- 可以将该成员看成为struct vm_page类型的数组,每个数组元素用来描述该物理内存段一个物理页框。
- first_page[0]用来描述该物理内存段内第一个物理页框。
- first_page[1]用来描述该物理内存段内第二个物理页框。
- first_page[2]用来描述该物理内存段内第三个物理页框。
- ...................................................
- 假设物理地址pa属于该内存段,那么其对应的页框描述的地址为&first_page[atop(pa - start)].
-
- free_queues:
- 指向vm_phys_free_queues[VM_FREELIST_DEFAULT]或者vm_phys_free_queues[VM_FREELIST_ISADMA],
- 相应物理内存段内的物理页框将被添加到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 };
复制代码 函数vm_phys_init执行完后,相关结构图如下:
[函数vm_phys_init]:- /***************************************************************************
- * Initialize the physical memory allocator.
- 根据"$FreeBSD: release/9.2.0/sys/i386/include/vmparam.h"中定义的
- 常量,下面的函数去掉了不会包含在代码中的部分,这样比较清晰。
- 完整函数可以在"$FreeBSD: release/9.2.0/sys/vm/vm_phys.c"中找到。
-
- #define VM_FREELIST_DEFAULT 0
- static int vm_nfreelists = VM_FREELIST_DEFAULT + 1;
- 变量vm_nfreelists为空闲物理页框队列的数目,从上面的定义来看,默认值
- 是1,在定义了VM_FREELIST_ISADMA时,该变量就会被更新为2.
- 变量vm_nfreelists和三维数组vm_phys_free_queues的第一维相关联。
- **********************************************/
- 294
- 295 void
- 296 vm_phys_init(void)
- 297 {
- 298 struct vm_freelist *fl;
- 299 int flind, i, oind, pind;
- /******************************************
- * 299-304:针对多个存储器域,这里忽略
- ********************/
- ...........................................................................................
- ...........................................................................................
- /***********************************************************************************************************
- * 304-337:
- 如果之前定义过VM_FREELIST_ISADMA,那么对于每个物理内存区域(phys_avail[i],phys_avail[i+1]),就要
- 检查其是否和DMA使用的物理内存有重叠(低16MB,16777216),如果有重叠,就要将这部分物理页框添加到
- 空闲物理页框队列vm_phys_free_queues[VM_FREELIST_ISADMA]中。
- 在"$FreeBSD: release/9.2.0/sys/i386/include/vmparam.h"文件中,已经对VM_FREELIST_ISADMA做出了定义。
- 为了分析方便,根据之前的假设,将以下面的参数调用函数vm_phys_create_seg:
- vm_phys_create_seg(phys_avail[0], 16777216,VM_FREELIST_ISADMA);
- vm_phys_create_seg(16777216, phys_avail[1],VM_FREELIST_DEFAULT);
- vm_phys_create_seg(phys_avail[2], phys_avail[3],VM_FREELIST_DEFAULT);
- vm_phys_create_seg(phys_avail[4], phys_avail[5],VM_FREELIST_DEFAULT);
- vm_phys_create_seg(phys_avail[6], phys_avail[7],VM_FREELIST_DEFAULT);
- 316-317:更新变量vm_nfreelists,更新后的值为2.
- *************************************************************************/
- 304 for (i = 0; phys_avail[i + 1] != 0; i += 2) {
- 305 #ifdef VM_FREELIST_ISADMA
- 306 if (phys_avail[i] < 16777216) {
- 307 if (phys_avail[i + 1] > 16777216) {
- 308 vm_phys_create_seg(phys_avail[i], 16777216,
- 309 VM_FREELIST_ISADMA);
- 310 vm_phys_create_seg(16777216, phys_avail[i + 1],
- 311 VM_FREELIST_DEFAULT);
- 312 } else {
- 313 vm_phys_create_seg(phys_avail[i],
- 314 phys_avail[i + 1], VM_FREELIST_ISADMA);
- 315 }
- 316 if (VM_FREELIST_ISADMA >= vm_nfreelists)
- 317 vm_nfreelists = VM_FREELIST_ISADMA + 1;
- 318 } else
- 319 #endif
- /********************************************************************
- * 319-335:高端内存相关,这里忽略
- 查了下,freebsd9.2中没有定义VM_FREELIST_HIGHMEM这个常量。
- 即三维数组vm_phys_free_queues第一维。
- ********************/
- ...........................................................................................
- ...........................................................................................
- 335 vm_phys_create_seg(phys_avail[i], phys_avail[i + 1],
- 336 VM_FREELIST_DEFAULT);
- 337 }
- /*****************************************************************************************
- * 338-344:
- 三维数组vm_phys_free_queues每个元素的类型为struct vm_freelist,下面的for循环对每个元素
- 的pl成员进行初始化。
- ************************************************/
- 338 for (flind = 0; flind < vm_nfreelists; flind++) {
- 339 for (pind = 0; pind < VM_NFREEPOOL; pind++) {
- 340 fl = vm_phys_free_queues[flind][pind];
- 341 for (oind = 0; oind < VM_NFREEORDER; oind++)
- 342 TAILQ_INIT(&fl[oind].pl);
- 343 }
- 344 }
- /******************************************
- * 344-373:针对多个存储器域,这里忽略。
- ********************/
- ...........................................................................................
- ...........................................................................................
- /***********************************************************************************************
- * 373-374:
- 初始化指针数组vm_phys_lookup_lists,该数组中每个元素指向一个二维数组,将做下面的初始化:
- vm_phys_lookup_lists[0][VM_FREELIST_DEFAULT] = &vm_phys_free_queues[VM_FREELIST_DEFAULT];
- vm_phys_lookup_lists[0][VM_FREELIST_ISADMA] = &vm_phys_free_queues[VM_FREELIST_ISADMA];
- 377:初始化相关的锁。
- *********************************************/
- 373 for (flind = 0; flind < vm_nfreelists; flind++)
- 374 vm_phys_lookup_lists[0][flind] = &vm_phys_free_queues[flind];
- 375
- 376
- 377 mtx_init(&vm_phys_fictitious_reg_mtx, "vmfctr", NULL, MTX_DEF);
- 378 }
复制代码 [函数vm_phys_create_seg]:- /************************************************************************************
- * 参数描述:
- start:相应物理内存区域的起始物理地址。
- end:相应物理内存区域的结尾物理地址。
- flind:
- 可以取值VM_FREELIST_DEFAULT或VM_FREELIST_ISADMA,即start和end之间的物理页框
- 将被添加到空闲物理页框队列vm_phys_free_queues[flind]中。
- 该函数实际上是函数_vm_phys_create_seg的简单封装,但是要先检查变量mem_affinity,
- 根据变量mem_affinity是否有效来确定传递给函数_vm_phys_create_seg的第4个参数
- (存储器域的编号)。
- *************************************/
- 263 static void
- 264 vm_phys_create_seg(vm_paddr_t start, vm_paddr_t end, int flind)
- 265 {
- 266 int i;
- 267
- /****************************************************************************************
- * struct mem_affinity {
- vm_paddr_t start; // 相关存储器域中一个物理内存区域的起始物理地址。
- vm_paddr_t end; // 相关存储器域中一个物理内存区域的结尾物理地址。
- int domain; // 上面start和end确定的物理内存区域所属的存储器域的编号。
- };
- struct mem_affinity *mem_affinity;
-
- struct mem_affinity mem_info[VM_PHYSSEG_MAX + 1];
- 只有VM_NDOMAIN大于1时,变量mem_affinity才会被初始化,在我们分析的i386平台上,
- VM_NDOMAIN为1,所以变量mem_affinity为NULL。
-
- 既然到这里了,就看看在VM_NDOMAIN大于1时,变量mem_affinity是如何被初始化的。
-
- 当VM_NDOMAIN大于1时:
- 在SI_SUB_VM对应的subsystem初始化之前,下面的初始化函数会先执行:
- SYSINIT(parse_srat, SI_SUB_VM - 1, SI_ORDER_FIRST, parse_srat, NULL);
- 函数parse_srat的任务就是分析ACPI定义的System Resource Affinity Table (SRAT),
- 这个SRAT是可选的,ACPI规范中对SRAT的定义如下:
- This optional table provides information that allows OSPM to associate processors
- and memory ranges, including ranges of memory provided by hot-added memory devices,
- with system localities / proximity domains and clock domains。
- SRAT里面包含了"A list of static resource allocation structures for the platform",
- 其中和存储器域相关的一个structures为"Memory Affinity Structure",描述如下:
- The Memory Affinity structure provides the following topology information statically
- to the operating system:
- 1->The association between a range of memory and the proximity domain to which it belongs
- 2->Information about whether the range of memory can be hot-plugged.
- 函数parse_srat会从"Memory Affinity Structure"提取出有意义的数据,并用这些数据初始化
- 数组mem_info,最后做一个简单的赋值:mem_affinity = mem_info;
-
- 有了上面的描述做准备,268-289行的代码已经很容易理解了。
- 在只有一个domain的情况下,此时mem_affinity为NULL:
- 此时传递给_vm_phys_create_seg的第四个参数始终未0.
- 在有多个domain的情况下,此时mem_affinity非空:
- 就要确定参数start和end确定的物理内存区域属于哪个domain,然后将这个domain的编号传递给
- 函数_vm_phys_create_seg。
- **************************************************/
- 268 if (mem_affinity == NULL) {
- 269 _vm_phys_create_seg(start, end, flind, 0);
- 270 return;
- 271 }
- 272
- 273 for (i = 0;; i++) {
- 274 if (mem_affinity[i].end == 0)
- 275 panic("Reached end of affinity info");
- 276 if (mem_affinity[i].end <= start)
- 277 continue;
- 278 if (mem_affinity[i].start > start)
- 279 panic("No affinity info for start %jx",
- 280 (uintmax_t)start);
- 281 if (mem_affinity[i].end >= end) {
- 282 _vm_phys_create_seg(start, end, flind,
- 283 mem_affinity[i].domain);
- 284 break;
- 285 }
- 286 _vm_phys_create_seg(start, mem_affinity[i].end, flind,
- 287 mem_affinity[i].domain);
- 288 start = mem_affinity[i].end;
- 289 }
- 290 }
复制代码 [函数_vm_phys_create_seg]:- /****************************************************************************************
- * Create a physical memory segment.
- 函数_vm_phys_create_seg前3个参数描述请参见函数vm_phys_create_seg。
- 参数domain:属于哪个存储器域,对于我们分析的i386平台,始终为0.
- i386平台,在"$FreeBSD: release/9.2.0/sys/i386/include/vmparam.h"文件
- 中有下面的定义:
- The physical address space is densely populated.
- #define VM_PHYSSEG_DENSE
- Only one memory domain.
- #define VM_NDOMAIN 1
- 为了不影响我们分析,将不会包含在函数中的代码忽略掉了。
- 函数_vm_phys_create_seg在"$FreeBSD: release/9.2.0/sys/vm/vm_phys.c"中可以找到。
- 函数PHYS_TO_VM_PAGE在"$FreeBSD: release/9.2.0/sys/vm/vm_page.c"中可以找到。
- 该函数将依次初始化数组vm_phys_segs中的元素,相应的数组元素用来描述start和end之间的
- 物理内存区域。
- 这里主要是一些赋值操作,当检查完数组phys_avail后,数组vm_phys_segs中的元素将被
- 初始化为:
- vm_phys_segs[0].start = phys_avail[0];
- vm_phys_segs[0].end = 16777216 ;
- vm_phys_segs[0].first_page = &vm_page_array[atop(phys_avail[0]) - first_page];
- vm_phys_segs[0].domain = 0
- vm_phys_segs[0].free_queues = &vm_phys_free_queues[VM_FREELIST_ISADMA];
- vm_phys_segs[1].start = 16777216;
- vm_phys_segs[1].end = phys_avail[1] ;
- vm_phys_segs[1].first_page = &vm_page_array[atop(16777216) - first_page];
- vm_phys_segs[1].domain = 0
- vm_phys_segs[1].free_queues = &vm_phys_free_queues[VM_FREELIST_DEFAULT];
- vm_phys_segs[2].start = phys_avail[2];
- vm_phys_segs[2].end = phys_avail[3] ;
- vm_phys_segs[2].first_page = &vm_page_array[atop(phys_avail[2]) - first_page];
- vm_phys_segs[2].domain = 0
- vm_phys_segs[2].free_queues = &vm_phys_free_queues[VM_FREELIST_DEFAULT];
- vm_phys_segs[3].start = phys_avail[4];
- vm_phys_segs[3].end = phys_avail[5] ;
- vm_phys_segs[3].first_page = &vm_page_array[atop(phys_avail[4]) - first_page];
- vm_phys_segs[3].domain = 0
- vm_phys_segs[3].free_queues = &vm_phys_free_queues[VM_FREELIST_DEFAULT];
- vm_phys_segs[4].start = phys_avail[6];
- vm_phys_segs[4].end = phys_avail[7] ;
- vm_phys_segs[4].first_page = &vm_page_array[atop(phys_avail[6]) - first_page];
- vm_phys_segs[4].domain = 0
- vm_phys_segs[4].free_queues = &vm_phys_free_queues[VM_FREELIST_DEFAULT];
- vm_phys_nsegs变量的值为5.
- ****************************/
- 227
- 228 static void
- 229 _vm_phys_create_seg(vm_paddr_t start, vm_paddr_t end, int flind, int domain)
- 230 {
- 231 struct vm_phys_seg *seg;
- 242 KASSERT(vm_phys_nsegs < VM_PHYSSEG_MAX,
- 243 ("vm_phys_create_seg: increase VM_PHYSSEG_MAX"));
- 244 seg = &vm_phys_segs[vm_phys_nsegs++];
- 245 seg->start = start;
- 246 seg->end = end;
- 247 seg->domain = domain;
- 251 seg->first_page = PHYS_TO_VM_PAGE(start);
- 260 seg->free_queues = &vm_phys_free_queues[flind];
- 261 }
复制代码 |
|