Chinaunix首页 | 论坛 | 博客
  • 博客访问: 172544
  • 博文数量: 60
  • 博客积分: 677
  • 博客等级: 上士
  • 技术积分: 667
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-30 15:49
文章分类

全部博文(60)

文章存档

2015年(1)

2013年(6)

2012年(16)

2011年(9)

2010年(28)

我的朋友

分类: BSD

2012-10-31 18:00:58

看FreeBSD内存管理的时候搜到这样的文章,但只有两个傻 逼doc网站(doc88和it168),收留了这文档,下载还要积分,原文不见了,所以努力找了一篇,方便大家。

本文版权归chishanmingshen所有,欢迎转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
作者:chishanmingshen 
博客:chishanmingshen.blog.chinaunix.net
======================================== 
BSD内存管理之amd64 by chishanmingshen 
之前的引导和加载略去.
elf64_exec(struct preloaded_file *fp)
 第一次设置页表:2M*512=1G空间的映射.将所有空间都映射到1G,其实只使用第一个1G.
 用于32bit模式处理.
 __exec((void *)VTOP(amd64_tramp), modulep, kernend);
 
amd64_tramp:
 设置cr3.打开分页机制.此时是32bit模式
 跳到64bit模式.(之前的entry_hi/entry_lo即btext地址)
 ljmp $0x8, $VTOP(longmode) 
locore.S
 建好一个bootstack
 1. call hammer_time(modulep,physfree)(其中
 会初始化中断,并建立0进程的pcb.将内核映像的名字拷贝到kernelname变量中.
 调用getmemsize(kmdp, physfree)->pmap_bootstrap()->create_pagetable().
 最后返回进程0的栈) 
 2. call mi_startup(module init) 
1.1 pmap_bootstrap(vm_paddr_t *firstaddr)/*入参其实就是kernend*/
 create_pagetables(firstaddr)为内核映像建立页表
 virtual_avail = (vm_offset_t) KERNBASE + *firstaddr;/*可见,之后都是用的这个虚拟地址来读内存.*/
 (#define KERNBASE KVADDR(KPML4I, KPDPI, 0, 0)/(511,510,0,0),就是最后1G空间.)
 virtual_end = VM_MAX_KERNEL_ADDRESS;/*虚拟地址的最大值:0xfFFFFFFFFFFFFFFF*/
 (#define VM_MAX_KERNEL_ADDRESS KVADDR(KPML4I, NPDPEPG-1, NKPDE-1, NPTEPG-1)/*511,510,511*/最后留了2M.)
 load_cr3(KPML4phys);从此进入KPML4phys时代
 /*kernel_pmap这个是最重要的,没有之一.它记录PML4表基址的虚拟地址,从物理地址KPML4phys开始.*/
 kernel_pmap->pm_pml4 = (pdp_entry_t *) (KERNBASE + KPML4phys); 
1.2 static void
create_pagetables(vm_paddr_t *firstaddr)
{
 int i; 
 /* Allocate pages */
 KPTphys = allocpages(firstaddr, NKPT);
 KPML4phys = allocpages(firstaddr, 1);
 KPDPphys = allocpages(firstaddr, NKPML4E);
 KPDphys = allocpages(firstaddr, NKPDPE); 
 ndmpdp = (ptoa(Maxmem) + NBPDP - 1) >> PDPSHIFT;
 if (ndmpdp < 4)  /* Minimum 4GB of dirmap */
  ndmpdp = 4;
 DMPDPphys = allocpages(firstaddr, NDMPML4E);
 DMPDphys = allocpages(firstaddr, ndmpdp);
 dmaplimit = (vm_paddr_t)ndmpdp << PDPSHIFT; 
 /* Fill in the underlying page table pages */
 /* Read-only from zero to physfree */
 /* XXX not fully used, underneath 2M pages */
 for (i = 0; (i << PAGE_SHIFT) < *firstaddr; i++) {/*1->PT表*/
  ((pt_entry_t *)KPTphys)[i] = i << PAGE_SHIFT;
  ((pt_entry_t *)KPTphys)[i] |= PG_RW | PG_V | PG_G | PG_U;
 } 
 /* Now map the page tables at their location within PTmap */
 for (i = 0; i < NKPT; i++) {/*2->PD表*/
  ((pd_entry_t *)KPDphys)[i] = KPTphys + (i << PAGE_SHIFT);
  ((pd_entry_t *)KPDphys)[i] |= PG_RW | PG_V | PG_U;
 } 
 /* Map from zero to end of allocations under 2M pages */
 /* This replaces some of the KPTphys entries above */
 for (i = 0; (i << PDRSHIFT) < *firstaddr; i++) {/*2->PD表,每项代表2M空间,直接覆盖上面2和1的赋值.*/
  ((pd_entry_t *)KPDphys)[i] = i << PDRSHIFT;
  ((pd_entry_t *)KPDphys)[i] |= PG_RW | PG_V | PG_PS | PG_G | PG_U;
 } 
 /* And connect up the PD to the PDP */
 for (i = 0; i < NKPDPE; i++) {/*3->PDP表*/
  ((pdp_entry_t *)KPDPphys)[i + KPDPI] = KPDphys + (i << PAGE_SHIFT);
  ((pdp_entry_t *)KPDPphys)[i + KPDPI] |= PG_RW | PG_V | PG_U | PG_U;
 } 

 /*4->DMPD表*/
 /* Now set up the direct map space using 2MB pages */
 for (i = 0; i < NPDEPG * ndmpdp; i++) {/*
  ((pd_entry_t *)DMPDphys)[i] = (vm_paddr_t)i << PDRSHIFT;
  ((pd_entry_t *)DMPDphys)[i] |= PG_RW | PG_V | PG_PS | PG_G | PG_U;
 }
 /*5->DMPDP表*/
 /* And the direct map space's PDP */
 for (i = 0; i < ndmpdp; i++) {
  ((pdp_entry_t *)DMPDPphys)[i] = DMPDphys + (i << PAGE_SHIFT);
  ((pdp_entry_t *)DMPDPphys)[i] |= PG_RW | PG_V | PG_U;
 } 
 /*最后,要设置KMPL4phys表的3个表项:递归表/直连表/内核表.*/
 /* And recursively map PML4 to itself in order to get PTmap */
 ((pdp_entry_t *)KPML4phys)[PML4PML4I] = KPML4phys;/*256 递归用*/
 ((pdp_entry_t *)KPML4phys)[PML4PML4I] |= PG_RW | PG_V | PG_U; 
 /* Connect the Direct Map slot up to the PML4 */
 ((pdp_entry_t *)KPML4phys)[DMPML4I] = DMPDPphys;/*510,倒数第二个512G */
 ((pdp_entry_t *)KPML4phys)[DMPML4I] |= PG_RW | PG_V | PG_U; 
 /* Connect the KVA slot up to the PML4 */
 ((pdp_entry_t *)KPML4phys)[KPML4I] = KPDPphys;/*511,最后一个512G */
 ((pdp_entry_t *)KPML4phys)[KPML4I] |= PG_RW | PG_V | PG_U;

  

2.void
mi_startup(void)
将所有注册的sysinit执行一遍,我们关注SI_SUB_VM模块. 
2.1 SYSINIT(vm_mem, SI_SUB_VM, SI_ORDER_FIRST, vm_mem_init, NULL);
vm_mem_init()
 vm_set_page_size设置页面大小为4k
 virtual_avail = vm_page_startup(virtual_avail);/*初始化各个物理页面,然后加入到freelist中*/
  遍历phys_avail[],得到段数:nblocks,总的空间大小:total.
  for (i = 0; i < PQ_COUNT; i++)/*初始化3个vm_page queue,各维护一个vm_page链表.*/
   TAILQ_INIT(&vm_page_queues[i].pl);
  vm_page_queues[PQ_INACTIVE].cnt = &cnt.v_inactive_count;
  vm_page_queues[PQ_ACTIVE].cnt = &cnt.v_active_count;
  vm_page_queues[PQ_HOLD].cnt = &cnt.v_active_count;
  end = phys_avail[biggestone+1];end先赋为最大可用的内存地址处,以下为预留扣除.
  扣去boot umaslab(大小为(boot_pages * UMA_SLAB_SIZE))
  得到new_end,将umaslab调pmap_map和uma_startup.(扣去的是由Dmap映射的,
  所以不需要递增vaddr.
   #define PHYS_TO_DMAP(x)  ((x) | DMAP_MIN_ADDRESS)
   #define DMAP_MIN_ADDRESS KVADDR(DMPML4I, 0, 0, 0)/*510, 倒数第二个512G.*/)
  接着扣去dump.
  phys_avail[biggestone + 1] = new_end;最后一段内存更正为到new_end结束,扣除了vm_page[],
  和slab,和dump.
  vm_page_array_size = page_range;
  vm_phys_init();/*初始化物理内存分配器*/
   对所有段调用vm_phys_create_seg(phys_avail[i], phys_avail[i + 1],VM_FREELIST_DEFAULT);
   更新到vm_phys_segs[]中.
   vm_phys_free_queues[vm_nfreelists][VM_NFREEPOOL]
   /*static int vm_nfreelists = VM_FREELIST_DEFAULT + 1;*/ 
  vaddr += PAGE_SIZE;/*vm_page_array[-1]置空映射,这样就造成一个保护空洞,在vm_page_array之前.*/
  new_end = trunc_page(end - page_range * sizeof(struct vm_page));
  mapped = pmap_map(&vaddr, new_end, end,
      VM_PROT_READ | VM_PROT_WRITE);
  vm_page_array = (vm_page_t) mapped; /*将预留的vm_page结构数组空间扣去.
  vm_page_array指向pmap_map()映射后的vm_page[]空间,共npages个页面.
  计算可用物理页面总数为page_range个,从而计算出对应的vm_page结构数组需要的页数.
  npages = (total - (page_range * sizeof(struct vm_page)) - (end - new_end)) / PAGE_SIZE;*/ 
  遍历phys_avail[],对所有物理页调用vm_phys_add_page(pa).
   vm_phys_add_page(pa/*vm_paddr 物理地址*/):初始化一个物理页面,同时将它加到free list中.
    m = vm_phys_paddr_to_vm_page(vm_paddr_t pa):/*找到给定物理地址对应的vm_page*/
     遍历vm_phys_segs[],找到对应的vm_page结构指针,并返回该指针.
     return &(seg->first_page[atop(pa - seg->start)]);   
    pmap_page_init(m);
    vm_phys_free_pages(m, 0);/*buddy算法,初始时都加到freelist中的单页队列,buddy里面会自动调整.*/    
  return (vaddr);
  /*最后将可以用的虚拟地址返回,其中vm_page[]的空间已经加进去了.
  返回的virtual_avail,由外面使用,即普通物理页面空间*/ 
阅读(1894) | 评论(2) | 转发(0) |
给主人留下些什么吧!~~

激光切割机2012-11-01 15:34:42