XV6(RISC-V)中sz为何可作为mappages的虚拟地址参数?
关于xv6中growproc与uvmalloc的地址疑问解答
首先看相关代码片段:
growproc函数
int growproc(int n) { uint sz; struct proc *p = myproc(); sz = p->sz; if(n > 0){ if((sz = uvmalloc(p->pagetable, sz, sz + n)) == 0) { return -1; } } else if(n < 0){ sz = uvmdealloc(p->pagetable, sz, sz + n); } p->sz = sz; return 0; }
uvmalloc函数
uint64 uvmalloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz) { char *mem; uint64 a; if(newsz < oldsz) return oldsz; oldsz = PGROUNDUP(oldsz); for(a = oldsz; a < newsz; a += PGSIZE){ mem = kalloc(); if(mem == 0){ uvmdealloc(pagetable, a, oldsz); return 0; } memset(mem, 0, PGSIZE); if(mappages(pagetable, a, PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U) != 0){ kfree(mem); uvmdealloc(pagetable, a, oldsz); return 0; } } return newsz; }
你的疑问核心是:为什么作为进程内存大小的sz,能当作mappages的虚拟地址参数?
这是因为xv6的用户进程地址空间设计特性:
- xv6的用户虚拟地址是从0地址开始向上连续分配的,进程的内存大小
sz(p->sz)同时也是用户虚拟地址空间的上边界——也就是说,sz的值刚好等于当前已分配虚拟地址的最高位置的下一个地址,也就是下一个要分配的虚拟页的起始地址。
举个具体例子:
假设当前进程的sz是4096(即PGSIZE,xv6中一页的大小),这意味着用户虚拟地址0~4095已经被分配使用,下一个要扩展的虚拟页的起始地址就是4096,也就是此时的sz值。
在uvmalloc中,oldsz被PGROUNDUP对齐后(确保是页大小的整数倍),循环变量a从这个对齐后的oldsz开始,每次递增PGSIZE,这个a就是明确的虚拟页起始地址,完全符合mappages对第二个参数(虚拟地址)的要求。
所以这里的sz身兼两职:既表示进程已用内存的总大小,也表示虚拟地址空间的下一个可用起始地址,因此可以直接作为虚拟地址传入mappages。
内容的提问来源于stack exchange,提问作者anru
相关产品推荐
相关产品推荐

