# 3.7: 系统调用sbrk的实现(Code: sbrk) `sbrk`是一个系统调用,用于进程调整(扩展或收缩)其内存大小。该系统调用的实现位于函数`growproc`中(kernel/proc.c:260)。 `growproc` 会调用 `uvmalloc` 或 `uvmdealloc`,取决于参数 n 是正数还是负数: ```c // Grow or shrink user memory by n bytes. // Return 0 on success, -1 on failure. int growproc(int n) { uint64 sz; struct proc *p = myproc(); sz = p->sz; if(n > 0){ if((sz = uvmalloc(p->pagetable, sz, sz + n, PTE_W)) == 0) { return -1; } } else if(n < 0){ sz = uvmdealloc(p->pagetable, sz, sz + n); } p->sz = sz; return 0; } ``` ## 如果 n 为正数:调用 uvmalloc(kernel/vm.c:233),分配物理内存。 ```c // Allocate PTEs and physical memory to grow process from oldsz to // newsz, which need not be page aligned. Returns new size or 0 on error. uint64 uvmalloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz, int xperm) { 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_R|PTE_U|xperm) != 0){ kfree(mem); uvmdealloc(pagetable, a, oldsz); return 0; } } return newsz; } ``` uvmalloc 的步骤包括: - 使用 kalloc 分配物理内存。 - 将分配的内存清零。 - 使用 mappages 将新的物理内存页面添加到用户页表中。 ## 如果 n 为负数:调用 uvmdealloc 来释放内存。 uvmdealloc 的步骤包括: - 调用 uvmunmap(kernel/vm.c:178)。 ```c // Remove npages of mappings starting from va. va must be // page-aligned. The mappings must exist. // Optionally free the physical memory. void uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free) { uint64 a; pte_t *pte; if((va % PGSIZE) != 0) panic("uvmunmap: not aligned"); for(a = va; a < va + npages*PGSIZE; a += PGSIZE){ if((pte = walk(pagetable, a, 0)) == 0) panic("uvmunmap: walk"); if((*pte & PTE_V) == 0) panic("uvmunmap: not mapped"); if(PTE_FLAGS(*pte) == PTE_V) panic("uvmunmap: not a leaf"); if(do_free){ uint64 pa = PTE2PA(*pte); kfree((void*)pa); } *pte = 0; } } ``` - uvmunmap 使用 walk 函数查找页表项(PTEs)。 - 使用 kfree 释放 PTE 所指向的物理内存。 ## 页表在内存管理中的作用 xv6 不仅仅将进程的页表用于指导硬件如何映射用户虚拟地址,还将其作为记录哪些物理内存页已分配给该进程的唯一来源。这也是为什么释放用户内存(在 uvmunmap 中)需要检查用户页表的原因。 ## 总结 sbrk 系统调用通过调用 growproc 来完成内存的增长或收缩,而 growproc 进一步依赖 uvmalloc 或 uvmdealloc 来实现实际的物理内存分配和释放。xv6 的设计选择将页表作为分配和管理物理内存的核心数据结构,既简化了设计,又确保了物理内存与虚拟内存的映射一致性。sbrk 的本质是通过调整进程的 "break"(堆的顶部边界) 来扩展或收缩数据段。数据段是进程的一个区域,通常用于动态内存分配,例如 malloc 等库函数就是基于 sbrk 实现的。