免费注册 查看新帖 |

Chinaunix

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

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

论坛徽章:
0
发表于 2014-07-16 00:33 |显示全部楼层
本帖最后由 71v5 于 2014-07-17 21:23 编辑

根据之前的描述:

函数getmemsize已经将数组phys_avail和dump_avail正确初始化,并且没有初始化的数组元素的值都为0,数组phys_avail用来描述系统中可用的
物理内存区域,该数组中每两个相邻元素描述一个物理内存区域,分别标识相应物理内存区域的起始物理地址和结尾物理地址,
比如(phys_avail[0],phys_avail[1]),(phys_avail[2],phys_avail[3]),(phys_avail[4],phys_avail[5]),(phys_avail[6],phys_avail[7])等等.
但是位于(KERNLOAD,first)之间的物理内存不由数组phys_avail来描述。

数组dump_avail和数组phys_avail类似,只不过数组dump_avail描述了(KERNLOAD,first)之间的物理内存。

在这里没有必要知道这些数组元素对应的具体物理地址,只需要明白这些数组元素描述了系统中的可用物理内存区域就可以了。

不过为了分析方便,做下面的假设:
1:
phys_avail描述了4个可用物理内存区域,即:(phys_avail[0],phys_avail[1]),(phys_avail[2],phys_avail[3]),
(phys_avail[4],phys_avail[5]),(phys_avail[6],phys_avail[7])
   
2:
物理内存区域(phys_avail[6],phys_avail[7]) 是最大的物理内存区域。

根据之前212号系统调用的输出结果来看,(phys_avail[6],phys_avail[7])可能不是最大的物理内存区域,这里完全是为了分析方便,
这种假设并不影响我们理解相关机制原理。

图1:
虚03.jpg

[内核虚拟地址空间布局]-图2:
虚02.jpg
虚01.jpg

[函数vm_page_startup]:
  1. /***************************************************************************************
  2. * 参数描述:
  3.    vaddr:内核可用的第一个虚拟线性地址。

  4.    该函数是在初始化函数vm_mem_init中调用的:
  5.    virtual_avail = vm_page_startup(virtual_avail);

  6.    全局变量virtual_avail,始终保存的是第一个可用的内核虚拟线性地址,动态更新,见上面
  7.    的图2。

  8.    该函数主要的工作:
  9.    1:
  10.    在物理内存区域(phys_avail[6],phys_avail[7])中分配一部分物理内存给内核其它部分使用,
  11.    其中最重要就是给数组vm_page_array分配物理内存。

  12.    2:
  13.    调用函数vm_phys_init根据数组phys_avail初始化数组vm_phys_segs,执行完后,系统中
  14.    的可用物理内存就通过数组vm_phys_segs来描述。

  15.    3:
  16.    调用函数vm_phys_add_page将空闲的物理页框添加到空闲物理页框队列的相应页框池中,
  17.    即三维数组vm_phys_free_queues。

  18.    4:
  19.    调用UMA相关的第一个初始化函数uma_startup,暂且略过。

  20.    为了使条理更清晰,上面第2步后面单独分析。
  21. ***************************************************/
  22.    245        vm_offset_t
  23.    246        vm_page_startup(vm_offset_t vaddr)
  24.    247        {
  25. /****************************************************************************
  26. *  248-260:
  27.     一些局部变量,结合下面的分析会更清楚这些局部变量的具体含义。
  28. ***********************************/
  29.    248                vm_offset_t mapped;
  30.    249                vm_paddr_t page_range;
  31.    250                vm_paddr_t new_end;
  32.    251                int i;
  33.    252                vm_paddr_t pa;
  34.    253                vm_paddr_t last_pa;
  35.    254                char *list;
  36.    255                
  37.    256                /* the biggest memory array is the second group of pages */
  38.    257                vm_paddr_t end;
  39.    258                vm_paddr_t biggestsize;
  40.    259                vm_paddr_t low_water, high_water;
  41.    260                int biggestone;
  42.    261                
  43.    262                biggestsize = 0;
  44.    263                biggestone = 0;
  45.    264                vaddr = round_page(vaddr);
  46.    265                
  47. /*****************************************************************************
  48. * #define trunc_page(x) ((x) & ~PAGE_MASK)
  49.    #define round_page(x) (((x) + PAGE_MASK) & ~PAGE_MASK)

  50.    宏trunc_page将x截尾取整,并按PAGE_SIZE对齐。
  51.    宏round_page将x向上取整,并按PAGE_SIZE对齐。
  52.    
  53.    266-269:
  54.    将数组phys_avail中的数组元素按PAGE_SIZE对齐。
  55. ********************************/
  56.    266                for (i = 0; phys_avail[i + 1]; i += 2) {
  57.    267                        phys_avail[i] = round_page(phys_avail[i]);
  58.    268                        phys_avail[i + 1] = trunc_page(phys_avail[i + 1]);
  59.    269                }
  60.    270                
  61. /****************************************************************************
  62. * 271-272:这里设置两个阈值。
  63.    low_water:可用的最小物理内存地址。
  64.    high_water:可用的最大物理内存地址。

  65.    274-285:
  66.    检查数组phys_avail,每次for循环,就检查一个物理内存区域(phys_avail[i],
  67.    phys_avail[i + 1)。
  68.    
  69.    275:局部变量size为正在检查的物理内存区域的大小。

  70.    277-280:
  71.    更新变量biggestone和biggestsize,biggestsize始终记录最大的物理内存区域
  72.    的大小,biggestone始终记录描述物理内存区域起始物理地址的数组元素在
  73.    数组phys_avail中的索引,即对应的i的值,这里就为6.

  74.    281-284:更新变量low_water和high_water。

  75.    287-289:XEN相关,这里忽略。   
  76. *****************************************/  
  77.    271                low_water = phys_avail[0];
  78.    272                high_water = phys_avail[1];
  79.    273                        
  80.    274                for (i = 0; phys_avail[i + 1]; i += 2) {
  81.    275                        vm_paddr_t size = phys_avail[i + 1] - phys_avail[i];
  82.    276       
  83.    277                        if (size > biggestsize) {
  84.    278                                biggestone = i;
  85.    279                                biggestsize = size;
  86.    280                        }
  87.    281                        if (phys_avail[i] < low_water)
  88.    282                                low_water = phys_avail[i];
  89.    283                        if (phys_avail[i + 1] > high_water)
  90.    284                                high_water = phys_avail[i + 1];
  91.    285                }
  92.    286       
  93.    287        #ifdef XEN
  94.    288                low_water = 0;
  95.    289        #endif       
  96.    290                
  97. /***********************************************************************************
  98. * 291:
  99.    将end设置为最大的物理内存区域的结尾物理地址,这里就为phys_avail[7]。            
  100. *****************************************/
  101.    291                end = phys_avail[biggestone+1];
  102.    292       
  103.    293               
  104.    294
  105. /********************************************************
  106. * Initialize the page and queue locks.
  107.    295-300:
  108.    初始化相关的锁,暂且忽略.
  109. ********************/  
  110.    295                 
  111.    296                mtx_init(&vm_page_queue_mtx, "vm page queue", NULL, MTX_DEF |
  112.    297                    MTX_RECURSE);
  113.    298                mtx_init(&vm_page_queue_free_mtx, "vm page free queue", NULL, MTX_DEF);
  114.    299                for (i = 0; i < PA_LOCK_COUNT; i++)
  115.    300                        mtx_init(&pa_lock[i].data, "vm page", NULL, MTX_DEF);
  116.    301       
  117.    302  
  118.    303
  119.    304
  120. /**********************************************************************
  121. * Initialize the queue headers for the hold queue, the active queue,
  122. * and the inactive queue.

  123.    初始化数组vm_page_queues。

  124.    struct vmmeter cnt;
  125.    cnt是内核定义的一个类型为struct vmmeter的数据对象,内核中这个结构体
  126.    的描述为"System wide statistics counters",即相当于一个系统范围内的
  127.    计数器:
  128.    u_int v_active_count;        /* (q) pages active */
  129.    u_int v_inactive_count;        /* (q) pages inactive */

  130.    308-310:
  131.    将相应数组vm_page_queues元素的cnt成员设置为cnt.v_active_count或者
  132.    cnt.v_inactive_count的地址,通过直接更新cnt.v_active_count等成员,
  133.    就可以从数组vm_page_queues的cnt成员来访问。      
  134. *********************************************/
  135.    305               
  136.    306                for (i = 0; i < PQ_COUNT; i++)
  137.    307                        TAILQ_INIT(&vm_page_queues[i].pl);
  138.    308                vm_page_queues[PQ_INACTIVE].cnt = &cnt.v_inactive_count;
  139.    309                vm_page_queues[PQ_ACTIVE].cnt = &cnt.v_active_count;
  140.    310                vm_page_queues[PQ_HOLD].cnt = &cnt.v_active_count;
  141.    311       
  142.    312               
  143.    313
  144.    314
  145. /***********************************************************************************
  146. * Allocate memory for use when boot strapping the kernel memory
  147. * allocator.

  148.    316-321:执行后所分配的物理内存可以参考上面图1中的"A"处。

  149.    #define UMA_SLAB_SIZE        PAGE_SIZE   How big are our slabs?
  150.    #define UMA_BOOT_PAGES  64        Pages allocated for startup
  151.    static int boot_pages = UMA_BOOT_PAGES;

  152.    316-317:
  153.    在物理内存区域(phys_avail[biggestone],phys_avail[biggestone + 1])中分配一段
  154.    大小为(64 * PAGE_SIZE)的物理内存,new_end为该物理内存区域新的结尾物理地址。
  155.    这里的话,物理内存区域就为(phys_avail[6],phys_avail[7].

  156.    318-319:
  157.    将这段物理内存映射到内核的虚拟地址空间中。

  158.    320:将[new_end,end]之间的物理内存清零

  159.    321:暂时略过对uma_startup函数的分析,UMA相关。   
  160. *****************************************************/              
  161.    315                 
  162.    316                new_end = end - (boot_pages * UMA_SLAB_SIZE);
  163.    317                new_end = trunc_page(new_end);
  164.    318                mapped = pmap_map(&vaddr, new_end, end,
  165.    319                    VM_PROT_READ | VM_PROT_WRITE);
  166.    320                bzero((void *)mapped, end - new_end);
  167.    321                uma_startup((void *)mapped, boot_pages);
  168.    322       
  169.    323        #if defined(__amd64__) || defined(__i386__) || defined(__arm__) || \
  170.    324            defined(__mips__)
  171. /**************************************************************************
  172. * Allocate a bitmap to indicate that a random physical page
  173. * needs to be included in a minidump.
  174. *
  175. * The amd64 port needs this to indicate which direct map pages
  176. * need to be dumped, via calls to dump_add_page()/dump_drop_page().
  177. *
  178. * However, i386 still needs this workspace internally within the
  179. * minidump code.  In theory, they are not needed on i386, but are
  180. * included should the sf_buf code decide to use them.

  181.    uint32_t *vm_page_dump;
  182.    int vm_page_dump_size;
  183.   
  184.    336-345:执行完后,所分配的物理内存可以参考上面图1中的"B"处。

  185.    在物理内存区域(phys_avail[biggestone],new_end])中分配一段大小为
  186.    vm_page_dump_size的物理内存。
  187.    1->这段物理内存由minidump code使用,其中每一个bit位对应系统中一个物理页框
  188.       (由数组dump_avail描述)。

  189.    2->将上面分配的物理内存映射到内核虚拟地址空间中,通过vm_page_dump
  190.       访问这段物理内存。

  191.    347-359:
  192.    针对AMD64位处理器,从这里看出,函数dump_add_page将之前分配给
  193.    system message buffer的物理页框对应的bit位设置为1,描述
  194.    物理页框的位图可以通过虚拟线性地址vm_page_dump访问。         
  195. ****************************************/
  196.    335               
  197.    336                last_pa = 0;
  198.    337                for (i = 0; dump_avail[i + 1] != 0; i += 2)
  199.    338                        if (dump_avail[i + 1] > last_pa)
  200.    339                                last_pa = dump_avail[i + 1];
  201.    340                page_range = last_pa / PAGE_SIZE;
  202.    341                vm_page_dump_size = round_page(roundup2(page_range, NBBY) / NBBY);
  203.    342                new_end -= vm_page_dump_size;
  204.    343                vm_page_dump = (void *)(uintptr_t)pmap_map(&vaddr, new_end,
  205.    344                    new_end + vm_page_dump_size, VM_PROT_READ | VM_PROT_WRITE);
  206.    345                bzero((void *)vm_page_dump, vm_page_dump_size);
  207.    346        #endif
  208.    347        #ifdef __amd64__
  209.    348                /*
  210.    349                 * Request that the physical pages underlying the message buffer be
  211.    350                 * included in a crash dump.  Since the message buffer is accessed
  212.    351                 * through the direct map, they are not automatically included.
  213.    352                 */
  214.    353                pa = DMAP_TO_PHYS((vm_offset_t)msgbufp->msg_ptr);
  215.    354                last_pa = pa + round_page(msgbufsize);
  216.    355                while (pa < last_pa) {
  217.    356                        dump_add_page(pa);
  218.    357                        pa += PAGE_SIZE;
  219.    358                }
  220.    359        #endif
  221. /*****************************************************************************************
  222. * Compute the number of pages of memory that will be available for
  223. * use (taking into account the overhead of a page structure per
  224. * page).

  225.    #define atop(x)        ((x) >> PAGE_SHIFT)

  226.    long first_page = 0;// 全局变量
  227.   
  228.    执行到这里:
  229.    low_water:系统中可用的最小物理内存地址。
  230.    high_water:系统中可用的最大物理内存地址

  231.    365:first_page为系统中可用的第一个物理页框号。
  232.    

  233.    page_range:系统中可用的物理页框的数目。

  234.    366-370:
  235.    如果之前定义过VM_PHYSSEG_SPARSE,即物理地址空间为稀疏模型,那么
  236.    page_range只记录了由数组phys_avail描述的物理页框的数目,不包括包含内核代码
  237.    数据等的物理页框。

  238.    370-372:
  239.    如果之前定义过VM_PHYSSEG_DENSE,即物理地址空间为密集模型。
  240.    那么page_range记录了系统中位于high_water之前的物理页框的数目,并且包括包含内核代码数据的物理页框,
  241.    被保留的物理页框等。

  242.    i386平台,freebsd9.2已经在"$FreeBSD: release/9.2.0/sys/i386/include/vmparam.h"
  243.    对VM_PHYSSEG_DENSE做出了定义,所以这里page_range选择370-372行之间重新设置后的值。
  244. *********************************************/  
  245.    364                 
  246.    365                first_page = low_water / PAGE_SIZE;
  247.    366        #ifdef VM_PHYSSEG_SPARSE
  248.    367                page_range = 0;
  249.    368                for (i = 0; phys_avail[i + 1] != 0; i += 2)
  250.    369                        page_range += atop(phys_avail[i + 1] - phys_avail[i]);
  251.    370        #elif defined(VM_PHYSSEG_DENSE)
  252.    371                page_range = high_water / PAGE_SIZE - first_page;
  253.    372        #else
  254.    373        #error "Either VM_PHYSSEG_DENSE or VM_PHYSSEG_SPARSE must be defined."
  255.    374        #endif
  256.    375                end = new_end;
  257.    376       
  258. /***********************************************************************
  259. * Reserve an unmapped guard page to trap access to vm_page_array[-1].
  260.    在内核虚拟地址空间中保留一个大小为PAGE_SIZE的地址区间
  261. **************************/
  262.    379                 
  263.    380                vaddr += PAGE_SIZE;
  264.    381       
  265. /*****************************************************************************************
  266. * Initialize the mem entry structures now, and put them in the free
  267. * queue.
  268.    typedef struct vm_page *vm_page_t;
  269.    vm_page_t vm_page_array;

  270.    386-389:
  271.    在物理内存区域(phys_avail[biggestone],end)分配一段物理内存,这段物理内存的
  272.    大小为page_range * sizeof(struct vm_page),用来保存描述系统中物理页框的页框描述符,
  273.    并将这段物理内存映射到内核地址空间上,通过虚拟线性地址vm_page_array访问这段物理内存。

  274.    执行到这里,就可以把vm_page_array看成是一个数组,数组元素的类型为struct vm_page,
  275.    数组元素的数目为page_range。

  276.    所分配的物理内存可以参考上面图1中的"C"处。
  277. ********************************************/
  278.    385                 
  279.    386                new_end = trunc_page(end - page_range * sizeof(struct vm_page));
  280.    387                mapped = pmap_map(&vaddr, new_end, end,
  281.    388                    VM_PROT_READ | VM_PROT_WRITE);
  282.    389                vm_page_array = (vm_page_t) mapped;
  283. /***********************************************************************************************
  284. * 390-395:
  285.    i386平台,freebsd9.2已经在"$FreeBSD: release/9.2.0/sys/i386/include/vmparam.h"
  286.    中做了下面的定义:
  287.    #define        VM_NRESERVLEVEL                1

  288.    static vm_reserv_t vm_reserv_array;

  289.    在物理内存区域(phys_avail[biggestone],new_end])分配一段物理内存,这段物理内存用来保存类型
  290.    为struct vm_reserv的数据对象,同时将这段物理内存映射到内核地址空间中,通过虚拟线性地址
  291.    vm_reserv_array来访问这段内存,这里也可把vm_reserv_array看作是一个类型为struct vm_reserv
  292.    的数组。

  293.    所分配的物理内存可以参考上面图1中的"D"处。
  294. ************************************************/   
  295.    390        #if VM_NRESERVLEVEL > 0
  296.    391                /*******************************************************************
  297.    392                 * Allocate memory for the reservation management system's data
  298.    393                 * structures.
  299.    394                 */
  300.    395                new_end = vm_reserv_startup(&vaddr, new_end, high_water);
  301.    396        #endif
  302. /**************************************************************************************
  303. * 397-406:针对AMD84或者MIPS
  304.    
  305.    执行到这里,new_end和phys_avail[biggestone + 1]之间的物理页框已经被分配,调用函数
  306.    dump_add_page将这部分物理页框对应的bit位设置为1,描述物理页框的位图可以通过虚拟线性
  307.    地址vm_page_dump来访问。
  308. *******************************/
  309.    397        #if defined(__amd64__) || defined(__mips__)
  310.    398                /*
  311.    399                 * pmap_map on amd64 and mips can come out of the direct-map, not kvm
  312.    400                 * like i386, so the pages must be tracked for a crashdump to include
  313.    401                 * this data.  This includes the vm_page_array and the early UMA
  314.    402                 * bootstrap pages.
  315.    403                 */
  316.    404                for (pa = new_end; pa < phys_avail[biggestone + 1]; pa += PAGE_SIZE)
  317.    405                        dump_add_page(pa);
  318.    406        #endif       
  319. /*************************************************************************************
  320. * 407:更新数组元素phys_avail[biggestone + 1],为物理内存区域新的结尾物理地址
  321. ********************************/
  322.    407                phys_avail[biggestone + 1] = new_end;
  323.    408       
  324. /*******************************************************************************
  325. * Clear all of the page structures
  326.    
  327.    412: 将数组vm_page_array清零。

  328.    413-414:将全部页框描述符的order成员设置为VM_NFREEORDER,在伙伴系统中使用.
  329.    
  330.    415:               
  331.    long vm_page_array_size;
  332.    vm_page_array_size:数组vm_page_array的大小,即页框描述符的数目,这里设置
  333.    为page_range。
  334. ********************************************/
  335.    411               
  336.    412                bzero((caddr_t) vm_page_array, page_range * sizeof(struct vm_page));
  337.    413                for (i = 0; i < page_range; i++)
  338.    414                        vm_page_array[i].order = VM_NFREEORDER;
  339.    415                vm_page_array_size = page_range;
  340.    416       
  341. /************************************************************************************
  342. * 417-441:
  343.    后续单独分析。
  344. *******************************/
  345.    417                /********************************************************************
  346.    418                 * Initialize the physical memory allocator.
  347.    419                 */
  348.    420                vm_phys_init();
  349.    421       
  350.    422                /*
  351.    423                 * Add every available physical page that is not blacklisted to
  352.    424                 * the free lists.
  353.    425                 */
  354.    426                cnt.v_page_count = 0;
  355.    427                cnt.v_free_count = 0;
  356.    428                list = getenv("vm.blacklist");
  357.    429                for (i = 0; phys_avail[i + 1] != 0; i += 2) {
  358.    430                        pa = phys_avail[i];
  359.    431                        last_pa = phys_avail[i + 1];
  360.    432                        while (pa < last_pa) {
  361.    433                                if (list != NULL &&
  362.    434                                    vm_page_blacklist_lookup(list, pa))
  363.    435                                        printf("Skipping page with pa 0x%jx\n",
  364.    436                                            (uintmax_t)pa);
  365.    437                                else
  366.    438                                        vm_phys_add_page(pa);
  367.    439                                pa += PAGE_SIZE;
  368.    440                        }
  369.    441                }
  370.    442                freeenv(list);
  371.    443        #if VM_NRESERVLEVEL > 0
  372. /*******************************************************************
  373. * Initialize the reservation management system.
  374.    大概看了一下vm_reserv_init函数,其初始化的struct vm_reserv
  375.    类型的数据对象跟内核其它部分联系比较紧密,单独分析没有实际
  376.    意义,后续遇到相关函数时再回头结合vm_reserv_init函数一起看看
  377. ***********************************/                 
  378.    446                 
  379.    447                vm_reserv_init();
  380.    448        #endif
  381.    449                return (vaddr);
  382.    450        }
复制代码

论坛徽章:
0
发表于 2015-12-23 12:00 |显示全部楼层
路过走一走。冒泡
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

DTCC2020中国数据库技术大会

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

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

http://dtcc.it168.com


大会官网>>
  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP