免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 5643 | 回复: 0

[FreeBSD] freebsd9.2-管理物理页框-初始化函数vm_phys_init [复制链接]

论坛徽章:
0
发表于 2014-07-17 00:44 |显示全部楼层
再来看看数组vm_phys_segs:
  1. /************************************************************************************************
  2. * #define        VM_PHYSSEG_MAX                17
  3.    static struct vm_phys_seg vm_phys_segs[VM_PHYSSEG_MAX]
  4.    static int vm_phys_nsegs;

  5.    函数vm_phys_init的主要工作是,对于由数组phys_avail描述的每一个物理内存区域,为其分配
  6.    一个类型为struct vm_phys_seg的数据对象,并做适当的初始化,该数据对象包含了关于这个物理内存
  7.    区域的更多附加信息。

  8.    变量vm_phys_nsegs在下面的_vm_phys_create_seg函数中使用,作为数组vm_phys_segs的索引.


  9.    成员描述:
  10.    start:
  11.    相应物理内存段的起始物理地址。

  12.    end:
  13.    相应物理内存段的结尾物理地址。

  14.    domain:
  15.    所属的存储器域。

  16.    first_page:
  17.    可以将该成员看成为struct vm_page类型的数组,每个数组元素用来描述该物理内存段一个物理页框。
  18.    first_page[0]用来描述该物理内存段内第一个物理页框。
  19.    first_page[1]用来描述该物理内存段内第二个物理页框。
  20.    first_page[2]用来描述该物理内存段内第三个物理页框。
  21.    ...................................................
  22.    假设物理地址pa属于该内存段,那么其对应的页框描述的地址为&first_page[atop(pa - start)].

  23.    free_queues:
  24.    指向vm_phys_free_queues[VM_FREELIST_DEFAULT]或者vm_phys_free_queues[VM_FREELIST_ISADMA],
  25.    相应物理内存段内的物理页框将被添加到free_queues指向的空闲物理页框队列中。
  26. ***********************************************************/
  27.     72        struct vm_phys_seg {
  28.     73                vm_paddr_t        start;
  29.     74                vm_paddr_t        end;
  30.     75                vm_page_t        first_page;
  31.     76                int                domain;
  32.     77                struct vm_freelist (*free_queues)[VM_NFREEPOOL][VM_NFREEORDER];
  33.     78        };
复制代码
函数vm_phys_init执行完后,相关结构图如下:
图05.png

[函数vm_phys_init]:
  1. /***************************************************************************
  2. * Initialize the physical memory allocator.

  3.    根据"$FreeBSD: release/9.2.0/sys/i386/include/vmparam.h"中定义的
  4.    常量,下面的函数去掉了不会包含在代码中的部分,这样比较清晰。
  5.    完整函数可以在"$FreeBSD: release/9.2.0/sys/vm/vm_phys.c"中找到。
  6.    
  7.    #define        VM_FREELIST_DEFAULT        0
  8.    static int vm_nfreelists = VM_FREELIST_DEFAULT + 1;
  9.    变量vm_nfreelists为空闲物理页框队列的数目,从上面的定义来看,默认值
  10.    是1,在定义了VM_FREELIST_ISADMA时,该变量就会被更新为2.
  11.    变量vm_nfreelists和三维数组vm_phys_free_queues的第一维相关联。
  12. **********************************************/
  13.    294         
  14.    295        void
  15.    296        vm_phys_init(void)
  16.    297        {
  17.    298                struct vm_freelist *fl;
  18.    299                int flind, i, oind, pind;
  19. /******************************************
  20. * 299-304:针对多个存储器域,这里忽略
  21. ********************/
  22. ...........................................................................................
  23. ...........................................................................................
  24. /***********************************************************************************************************
  25. * 304-337:
  26.    如果之前定义过VM_FREELIST_ISADMA,那么对于每个物理内存区域(phys_avail[i],phys_avail[i+1]),就要
  27.    检查其是否和DMA使用的物理内存有重叠(低16MB,16777216),如果有重叠,就要将这部分物理页框添加到
  28.    空闲物理页框队列vm_phys_free_queues[VM_FREELIST_ISADMA]中。

  29.    在"$FreeBSD: release/9.2.0/sys/i386/include/vmparam.h"文件中,已经对VM_FREELIST_ISADMA做出了定义。

  30.    为了分析方便,根据之前的假设,将以下面的参数调用函数vm_phys_create_seg:
  31.    vm_phys_create_seg(phys_avail[0], 16777216,VM_FREELIST_ISADMA);
  32.    vm_phys_create_seg(16777216, phys_avail[1],VM_FREELIST_DEFAULT);
  33.    vm_phys_create_seg(phys_avail[2], phys_avail[3],VM_FREELIST_DEFAULT);
  34.    vm_phys_create_seg(phys_avail[4], phys_avail[5],VM_FREELIST_DEFAULT);
  35.    vm_phys_create_seg(phys_avail[6], phys_avail[7],VM_FREELIST_DEFAULT);

  36.    316-317:更新变量vm_nfreelists,更新后的值为2.
  37. *************************************************************************/
  38.    304                for (i = 0; phys_avail[i + 1] != 0; i += 2) {
  39.    305        #ifdef        VM_FREELIST_ISADMA
  40.    306                        if (phys_avail[i] < 16777216) {
  41.    307                                if (phys_avail[i + 1] > 16777216) {
  42.    308                                        vm_phys_create_seg(phys_avail[i], 16777216,
  43.    309                                            VM_FREELIST_ISADMA);
  44.    310                                        vm_phys_create_seg(16777216, phys_avail[i + 1],
  45.    311                                            VM_FREELIST_DEFAULT);
  46.    312                                } else {
  47.    313                                        vm_phys_create_seg(phys_avail[i],
  48.    314                                            phys_avail[i + 1], VM_FREELIST_ISADMA);
  49.    315                                }
  50.    316                                if (VM_FREELIST_ISADMA >= vm_nfreelists)
  51.    317                                        vm_nfreelists = VM_FREELIST_ISADMA + 1;
  52.    318                        } else
  53.    319        #endif
  54. /********************************************************************
  55. * 319-335:高端内存相关,这里忽略
  56.    查了下,freebsd9.2中没有定义VM_FREELIST_HIGHMEM这个常量。
  57.    即三维数组vm_phys_free_queues第一维。
  58. ********************/
  59. ...........................................................................................
  60. ...........................................................................................
  61.    335                        vm_phys_create_seg(phys_avail[i], phys_avail[i + 1],
  62.    336                            VM_FREELIST_DEFAULT);
  63.    337                }
  64. /*****************************************************************************************
  65. * 338-344:
  66.    三维数组vm_phys_free_queues每个元素的类型为struct vm_freelist,下面的for循环对每个元素
  67.    的pl成员进行初始化。
  68. ************************************************/
  69.    338                for (flind = 0; flind < vm_nfreelists; flind++) {
  70.    339                        for (pind = 0; pind < VM_NFREEPOOL; pind++) {
  71.    340                                fl = vm_phys_free_queues[flind][pind];
  72.    341                                for (oind = 0; oind < VM_NFREEORDER; oind++)
  73.    342                                        TAILQ_INIT(&fl[oind].pl);
  74.    343                        }
  75.    344                }
  76. /******************************************
  77. * 344-373:针对多个存储器域,这里忽略。
  78. ********************/
  79. ...........................................................................................
  80. ...........................................................................................
  81. /***********************************************************************************************
  82. * 373-374:
  83.    初始化指针数组vm_phys_lookup_lists,该数组中每个元素指向一个二维数组,将做下面的初始化:
  84.    vm_phys_lookup_lists[0][VM_FREELIST_DEFAULT] = &vm_phys_free_queues[VM_FREELIST_DEFAULT];
  85.    vm_phys_lookup_lists[0][VM_FREELIST_ISADMA] = &vm_phys_free_queues[VM_FREELIST_ISADMA];

  86.    377:初始化相关的锁。
  87. *********************************************/
  88.    373                for (flind = 0; flind < vm_nfreelists; flind++)
  89.    374                        vm_phys_lookup_lists[0][flind] = &vm_phys_free_queues[flind];
  90.    375       
  91.    376       
  92.    377                mtx_init(&vm_phys_fictitious_reg_mtx, "vmfctr", NULL, MTX_DEF);
  93.    378        }
复制代码
[函数vm_phys_create_seg]:
  1. /************************************************************************************
  2. * 参数描述:
  3.    start:相应物理内存区域的起始物理地址。

  4.    end:相应物理内存区域的结尾物理地址。

  5.    flind:
  6.    可以取值VM_FREELIST_DEFAULT或VM_FREELIST_ISADMA,即start和end之间的物理页框
  7.    将被添加到空闲物理页框队列vm_phys_free_queues[flind]中。

  8.    该函数实际上是函数_vm_phys_create_seg的简单封装,但是要先检查变量mem_affinity,
  9.    根据变量mem_affinity是否有效来确定传递给函数_vm_phys_create_seg的第4个参数
  10.    (存储器域的编号)。
  11. *************************************/
  12.    263        static void
  13.    264        vm_phys_create_seg(vm_paddr_t start, vm_paddr_t end, int flind)
  14.    265        {
  15.    266                int i;
  16.    267                
  17. /****************************************************************************************
  18. *  struct mem_affinity {
  19.               vm_paddr_t start; // 相关存储器域中一个物理内存区域的起始物理地址。
  20.               vm_paddr_t end; // 相关存储器域中一个物理内存区域的结尾物理地址。
  21.               int domain; // 上面start和end确定的物理内存区域所属的存储器域的编号。
  22.     };
  23.     struct mem_affinity *mem_affinity;
  24.    
  25.     struct mem_affinity mem_info[VM_PHYSSEG_MAX + 1];

  26.     只有VM_NDOMAIN大于1时,变量mem_affinity才会被初始化,在我们分析的i386平台上,
  27.     VM_NDOMAIN为1,所以变量mem_affinity为NULL。
  28.    
  29.     既然到这里了,就看看在VM_NDOMAIN大于1时,变量mem_affinity是如何被初始化的。
  30.    
  31.     当VM_NDOMAIN大于1时:
  32.     在SI_SUB_VM对应的subsystem初始化之前,下面的初始化函数会先执行:
  33.     SYSINIT(parse_srat, SI_SUB_VM - 1, SI_ORDER_FIRST, parse_srat, NULL);
  34.     函数parse_srat的任务就是分析ACPI定义的System Resource Affinity Table (SRAT),
  35.     这个SRAT是可选的,ACPI规范中对SRAT的定义如下:
  36.     This optional table provides information that allows OSPM to associate processors
  37.     and memory ranges, including ranges of memory provided by hot-added memory devices,
  38.     with system localities / proximity domains and clock domains。
  39.     SRAT里面包含了"A list of static resource allocation structures for the platform",
  40.     其中和存储器域相关的一个structures为"Memory Affinity Structure",描述如下:
  41.     The Memory Affinity structure provides the following topology information statically
  42.     to the operating system:
  43.     1->The association between a range of memory and the proximity domain to which it belongs
  44.     2->Information about whether the range of memory can be hot-plugged.
  45.     函数parse_srat会从"Memory Affinity Structure"提取出有意义的数据,并用这些数据初始化
  46.     数组mem_info,最后做一个简单的赋值:mem_affinity = mem_info;
  47.       
  48.     有了上面的描述做准备,268-289行的代码已经很容易理解了。

  49.     在只有一个domain的情况下,此时mem_affinity为NULL:
  50.     此时传递给_vm_phys_create_seg的第四个参数始终未0.

  51.     在有多个domain的情况下,此时mem_affinity非空:
  52.     就要确定参数start和end确定的物理内存区域属于哪个domain,然后将这个domain的编号传递给
  53.     函数_vm_phys_create_seg。   
  54. **************************************************/         
  55.    268                if (mem_affinity == NULL) {
  56.    269                        _vm_phys_create_seg(start, end, flind, 0);
  57.    270                        return;
  58.    271                }
  59.    272       
  60.    273                for (i = 0;; i++) {
  61.    274                        if (mem_affinity[i].end == 0)
  62.    275                                panic("Reached end of affinity info");
  63.    276                        if (mem_affinity[i].end <= start)
  64.    277                                continue;
  65.    278                        if (mem_affinity[i].start > start)
  66.    279                                panic("No affinity info for start %jx",
  67.    280                                    (uintmax_t)start);
  68.    281                        if (mem_affinity[i].end >= end) {
  69.    282                                _vm_phys_create_seg(start, end, flind,
  70.    283                                    mem_affinity[i].domain);
  71.    284                                break;
  72.    285                        }
  73.    286                        _vm_phys_create_seg(start, mem_affinity[i].end, flind,
  74.    287                            mem_affinity[i].domain);
  75.    288                        start = mem_affinity[i].end;
  76.    289                }
  77.    290        }
复制代码
[函数_vm_phys_create_seg]:
  1. /****************************************************************************************
  2. * Create a physical memory segment.

  3.    函数_vm_phys_create_seg前3个参数描述请参见函数vm_phys_create_seg。
  4.    参数domain:属于哪个存储器域,对于我们分析的i386平台,始终为0.

  5.    i386平台,在"$FreeBSD: release/9.2.0/sys/i386/include/vmparam.h"文件
  6.    中有下面的定义:
  7.    The physical address space is densely populated.
  8.    #define        VM_PHYSSEG_DENSE

  9.    Only one memory domain.
  10.    #define        VM_NDOMAIN                1

  11.    为了不影响我们分析,将不会包含在函数中的代码忽略掉了。
  12.    函数_vm_phys_create_seg在"$FreeBSD: release/9.2.0/sys/vm/vm_phys.c"中可以找到。
  13.    函数PHYS_TO_VM_PAGE在"$FreeBSD: release/9.2.0/sys/vm/vm_page.c"中可以找到。

  14.    该函数将依次初始化数组vm_phys_segs中的元素,相应的数组元素用来描述start和end之间的
  15.    物理内存区域。

  16.    这里主要是一些赋值操作,当检查完数组phys_avail后,数组vm_phys_segs中的元素将被
  17.    初始化为:
  18.    vm_phys_segs[0].start = phys_avail[0];
  19.    vm_phys_segs[0].end = 16777216 ;
  20.    vm_phys_segs[0].first_page = &vm_page_array[atop(phys_avail[0]) - first_page];
  21.    vm_phys_segs[0].domain = 0
  22.    vm_phys_segs[0].free_queues = &vm_phys_free_queues[VM_FREELIST_ISADMA];

  23.    vm_phys_segs[1].start = 16777216;
  24.    vm_phys_segs[1].end = phys_avail[1] ;
  25.    vm_phys_segs[1].first_page = &vm_page_array[atop(16777216) - first_page];
  26.    vm_phys_segs[1].domain = 0
  27.    vm_phys_segs[1].free_queues = &vm_phys_free_queues[VM_FREELIST_DEFAULT];

  28.    vm_phys_segs[2].start = phys_avail[2];
  29.    vm_phys_segs[2].end = phys_avail[3] ;
  30.    vm_phys_segs[2].first_page = &vm_page_array[atop(phys_avail[2]) - first_page];
  31.    vm_phys_segs[2].domain = 0
  32.    vm_phys_segs[2].free_queues = &vm_phys_free_queues[VM_FREELIST_DEFAULT];

  33.    vm_phys_segs[3].start = phys_avail[4];
  34.    vm_phys_segs[3].end = phys_avail[5] ;
  35.    vm_phys_segs[3].first_page = &vm_page_array[atop(phys_avail[4]) - first_page];
  36.    vm_phys_segs[3].domain = 0
  37.    vm_phys_segs[3].free_queues = &vm_phys_free_queues[VM_FREELIST_DEFAULT];   

  38.    vm_phys_segs[4].start = phys_avail[6];
  39.    vm_phys_segs[4].end = phys_avail[7] ;
  40.    vm_phys_segs[4].first_page = &vm_page_array[atop(phys_avail[6]) - first_page];
  41.    vm_phys_segs[4].domain = 0
  42.    vm_phys_segs[4].free_queues = &vm_phys_free_queues[VM_FREELIST_DEFAULT];  

  43.    vm_phys_nsegs变量的值为5.
  44. ****************************/
  45.    227         
  46.    228        static void
  47.    229        _vm_phys_create_seg(vm_paddr_t start, vm_paddr_t end, int flind, int domain)
  48.    230        {
  49.    231                struct vm_phys_seg *seg;
  50.    242                KASSERT(vm_phys_nsegs < VM_PHYSSEG_MAX,
  51.    243                    ("vm_phys_create_seg: increase VM_PHYSSEG_MAX"));
  52.    244                seg = &vm_phys_segs[vm_phys_nsegs++];
  53.    245                seg->start = start;
  54.    246                seg->end = end;
  55.    247                seg->domain = domain;
  56.    251                seg->first_page = PHYS_TO_VM_PAGE(start);
  57.    260                seg->free_queues = &vm_phys_free_queues[flind];
  58.    261        }
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

DTCC2020中国数据库技术大会

【架构革新 高效可控】2020年12月21日-23日第十一届中国数据库技术大会将在北京隆重召开。

大会设置2大主会场,20+技术专场,将邀请超百位行业专家,重点围绕数据架构、AI与大数据、传统企业数据库实践和国产开源数据库等内容展开分享和探讨,为广大数据领域从业人士提供一场年度盛会和交流平台。

http://dtcc.it168.com


大会官网>>
  

北京盛拓优讯信息技术有限公司. 版权所有 16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122
中国互联网协会会员  联系我们:huangweiwei@it168.com
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP