01 内存管理
内存管理:通过编写物理页分配器,以链表管理所有空闲页帧, 实现了对物理页帧的回收与分配;在xv6系统sbrk内存管理方式的基础上,添加了进程用户空间非连续分区的分配。
内存管理参考链接
mmap
02 sbrk机制
描述:
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)]
03 myalloc/myfree机制
xv6内存管理方法:sbrk机制
在学习完xv6的进程分配和释放内存的方式后,发现xv6只允许扩展和收缩进程空间,也就是采用了linux中提供的sbrk机制。
- sys_sbrk()将进一步调用growproc(n)进行内存空间的调整 , proc.c中的growproc(n)底层实现原理就是根据传入参数n的正负进行扩展和收缩内存
- kalloc()会分配一个物理页帧,kfree()会释放一个物理页帧,mappages()用于建立虚存地址和物理页帧之间的关系
- 如何实现Linux的alloc()和free()
myalloc实现
进行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实现
进行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 => 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();
}