内存管理:通过编写物理页分配器,以链表管理所有空闲页帧, 实现了对物理页帧的回收与分配;在xv6系统sbrk内存管理方式的基础上,添加了进程用户空间非连续分区的分配。


内存管理参考链接
mmap
描述:
brk()和sbrk()改变程序间断点的位置。程序间断点就是程序数据段的结尾。(程序间断点是为初始化数据段的起始位置).通过增加程序间断点进程可以更有效的申请内存 。当addr参数合理、系统有足够的内存并且不超过最大值时brk()函数将数据段结尾设置为addr,即间断点设置为addr。sbrk()将程序数据空间增加increment字节。当increment为0时则返回程序间断点的当前位置。
返回值:
brk()成功返回0,失败返回-1并且设置errno值为ENOMEM(注:在mmap中会提到)。
sbrk()成功返回之前的程序间断点地址。如果间断点值增加,那么这个指针(指的是返回的之前的间断点地址)是指向分配的新的内存的首地址。如果出错失败,就返回一个指针并设置errno全局变量的值为ENOMEM。
总结:
这两个函数都用来改变 “program break” (程序间断点)的位置,改变数据段长度(Change data segment size),实现虚拟内存到物理内存的映射。
brk()函数直接修改有效访问范围的末尾地址实现分配与回收。sbrk()参数函数中:当increment为正值时,间断点位置向后移动increment字节。同时返回移动之前的位置,相当于分配内存。当increment为负值时,位置向前移动increment字节,相当与于释放内存,其返回值没有实际意义。当increment为0时,不移动位置只返回当前位置。参数increment的符号决定了是分配还是回收内存。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nUPoIDFQ-1668171558979)(https://image-1312312327.cos.ap-shanghai.myqcloud.com/20170116174212008)]
在学习完xv6的进程分配和释放内存的方式后,发现xv6只允许扩展和收缩进程空间,也就是采用了linux中提供的sbrk机制。
进行myalloc分配内存的时候
(1)需要查找合适的地址范围,并创建vma进行描述
在sbrk调用的基础上,找到size,然后myalloc传入的参数n就是要分配内存空间的大小,继续往上的vm空间查找合适的地址范围
(2)要用kalloc分配足够的物理页帧,并用mappages()将这些页帧映射到指定的虚存地址上
找到对应的地址范围后,分配物理页帧,同时进行页表映射,把物理页帧映射到虚存地址上去
int
mygrowproc(int n){ // 实现首次最佳适应算法struct vma *vm = proc->vm; // 遍历寻找合适的空间int start = proc->sz; // 寻找合适的分配起点int index;int prev = 0;int i;for(index = vm[0].next; index != 0; index = vm[index].next){if(start + n < vm[index].address)break;start = vm[index].address + vm[index].length;prev = index;}for(i = 1; i < 10; i++) { // 寻找一块没有用的 vma 记录新的内存块if(vm[i].next == -1){vm[i].next = index; vm[i].address = start;vm[i].length = n;vm[prev].next = i;myallocuvm(proc->pgdir, start, start + n);switchuvm(proc);return start; // 返回分配的地址}}switchuvm(proc);return 0;
}int
myallocuvm(pde_t *pgdir, uint start, uint end) {char* mem;uint a;a = PGROUNDUP(start);for(; a < end; a += PGSIZE) {mem = kalloc();memset(mem, 0, PGSIZE);mappages(pgdir, (char*)a, PGSIZE, V2P(mem), PTE_W|PTE_U);}return (end-start);
}
进行myfree释放内存的时候
(1)需要释放所涉及的页帧,逐个解除页表映射
(2)删除vma
int
myreduceproc(int address){ // 释放 address 开头的内存块int prev = 0;int index;for(index = proc->vm[0].next; index != 0; index = proc->vm[index].next) {if(proc->vm[index].address == address && proc->vm[index].length > 0) {mydeallocuvm(proc->pgdir, proc->vm[index].address, proc->vm[index].address + proc->vm[index].length); proc->vm[prev].next = proc->vm[index].next;proc->vm[index].next = -1;proc->vm[index].length = 0;break;}prev = index;}switchuvm(proc);return 0;
}int
mydeallocuvm(pde_t *pgdir, uint start, uint end) {pte_t *pte;uint a, pa;a = PGROUNDUP(start);for(; a < end; a += PGSIZE) {pte = walkpgdir(pgdir, (char*)a, 0);if(!pte)a += (NPTENTRIES - 1) * PGSIZE;else if((*pte & PTE_P) != 0){pa = PTE_ADDR(*pte);if(pa == 0)panic("kfree");char *v = P2V(pa);kfree(v);*pte = 0;}}return 1;
}
myalloc => myalloc、myfree => mygrowproc、myreduceproc => myallocuvm、mydeallocuvm
#include "types.h"
#include "stat.h"
#include "user.h"int
main(int argc, char *argv[]) {// int pid = getpid(); // map(pid);char* m1 = (char*)myalloc(2 * 4096);char* m2 = (char*)myalloc(3 * 4096);char* m3 = (char*)myalloc(1 * 4096);char* m4 = (char*)myalloc(7 * 4096);char* m5 = (char*)myalloc(9 * 4096);m1[0] = 'h';m1[1] = '\0';printf(1,"m1:%s\n",m1);myfree(m2);//m2[1] = 'p';myfree(m4);// map(pid);sleep(5000);myfree(m1);myfree(m3);myfree(m5);// char *p=(char *)0x0000;// for(int i=0x0000;i<0x08;i++)// *(p+i)='*';// printf(1,"This string shouldn't be modified!\n");// exit();exit();
}