实验五 内存管理(在xv6系统中实现:修改内存布局,将堆栈移动到地址空间的顶部并实现堆)

实验五 内存管理

一、实验目的与内容

在xv6系统中实现:修改内存布局,将堆栈移动到地址空间的顶部并实现堆
栈增长。

二、实验环境

Linux 环境使用 Ubuntu 虚拟机。

三、代码学习及实验过程

(一)、代码阅读与学习
程序的内存映射取决于我们如何将程序加载到内存中并设置页表(从而使它们指向正确的物理页)。这都是在exec.c中实现的,作为exec系统调用的一部分,使用vm.c中提供的底层支持来实现虚拟内存。因此要更改内存布局,我们必须更改exec代码以以我们想要的新方式加载程序并分配堆栈。具体来说,
首先打开exec.c检查执行系统调用的exec(…)函数。Exec执行以下操作:
(1)打开可执行文件并解析它。这一段的其余部分仅供参考。通常,可执
行文件由一个包含信息的头组成,这些信息允许我们索引文件的其余部分。之
后的文件由部分组成,包括代码、全局数据,有时还有其他部分,如未初始化
的数据。这些是我们需要从可执行文件中初始化的内存部分。头信息包括节的
数量、文件中每个节的开始、它在虚拟内存中的映射位置以及每个节的长度。
(2)使用setupkvm()初始化内核内存,它将内核的页面映射到进程地址空
间。
(3)然后,它继续使用loaduvm()将可执行文件的各个节加载到内存中,loaduvm()为每个节创建内存页,并将它们映射到地址空间。然后,我们可以方便地跟踪用户地址空间的结束位置,这也使用一个值定义了进程的大小(proc->sz)。因此,当我们映射新页面时,sz(向上四舍五入到下一页)可以作为它们的虚拟地址,因为我们只是按顺序填充地址空间。
我们希望从用户地址空间的顶部开始定位堆栈,以便给它空间来增长。
allocuvm它有3个参数:
(1)页表(pgdir)。
(2)我们正在映射的第一个页面的虚拟地址——这需要更改为指向内存中
用户部分的顶部页面(就在KERNBASE下面)。
(3)我们正在映射的最后一个页面的虚拟地址。对于我们来说,我们创建
的堆栈只有一个页面,这样就可以在同一页面中创建一个比第一个地址稍大的
地址。
allocuvm分配页面,并将其映射到页表。所以,基本上我们已经完成了移动栈的操作,只需要将这些参数更改为正确的值。
让我们深入理解下allocuvm。
allocuvm、deallocuvm(allocate user virtual memory)负责完成用户进程的内存空间分配和回收。allocuvm设置页表来分配物理内存供用户进程使用。其中第一个参数pgdir(page directory)指向一个页目录表,后两个参数oldsz,newsz,是分配虚拟地址oldsz到newsz的以页为单位的内存;deallocuvm则相反,它将newsz到oldsz对应的虚拟地址空间内存置为空闲。可以在下面的代码中看到,默认这两个函数是专门分配在KERNBASE以下的栈的(即用户空间)。
在这里插入图片描述

(二)、完成用户栈的移动。
1、在proc.h中对proc结构体当中添加信息。
Added a field ‘stackPages’ in the proc struct which stores the number of pages
allocated to a process stack:
在这里插入图片描述

2、memlayout.h
Defined a variable “STACKBASE” which points to one address lower than the
KERNBASE. This will be the base of our newly shifted stack:
#define STACKBASE (KERNBASE - 1)
3、exec.c
After we round up sz once code and data are loaded, we allocate 1 page size memory
for the new stack starting from STACKBASE – PGSIZE to STACKBASE. Then we assign the STACKBASE to the stack pointer sp:
在这里插入图片描述

4、修改vm.c中的copyuvm函数,用于子进程创建时虚拟地址空间复制我们新的“for”循环从STACKBASE开始,一直到栈顶。堆栈顶部的计算方法是从STACKBASE中减去“堆栈页数乘以页面大小”。我们正在按页面大小递减循环计数器的值,因为我们按1个页面大小复制内存。循环体与复制代码和数据块的“for”循环体相同。
在这里插入图片描述

(三)、实现栈的内容增长
1、修改 trap.c 文件的内容,追加一个新的陷阱号 T_PGFLT,接着在 trap.c中添加 case 语句,当出现页错误时,用 rcr2()获取 CR 寄存器中存放的地址,这个地址就是当前进程要申请的地址,接下来检查这个地址是不是正好在旧的栈的栈底(是不是在保护页中),当在保护页中时,需要通过调用allocuvm 增长当前的栈,同时将栈空间的页总数计数器加一。
在这里插入图片描述

2、Modified proc.c for copying stackPages field from parent process to child process in fork().
在这里插入图片描述

3、Modified syscall.c to take care of some system calls – fetchint(), fetchstr(),
and argptr().

int fetchint(uint addr, int *ip) {
//Commenting following curproc for lab3 as it is not needed.
//struct proc *curproc = myproc();
//Commenting following 'if' and adding another 'if' for lab3.
//if(addr >= curproc->sz || addr+4 > curproc->sz)
if(addr > STACKBASE || addr+4 > STACKBASE)
return -1;
*ip = *(int*)(addr);
return 0;
}
int fetchstr(uint addr, char **pp) {
char *s, *ep;
//Commenting following curproc for lab3 as it is not needed.
//struct proc *curproc = myproc();
//Commenting following 'if' for lab3 and added another 'if' containing
//STACKBASE
//if(addr >= curproc->sz)
if(addr > STACKBASE)
return -1;
*pp = (char*)addr;
//Commenting following ep assignment and adding another for lab3.
//ep = (char*)curproc->sz;
ep = (char*) STACKBASE;
//Commenting following 'for' for lab3 and adding another 'for'.
//for(s = *pp; s < ep; s++){
for(s = *pp; s <= ep; s++) {
if(*s == 0)
return s - *pp;
}
return -1;
}
int argptr(int n, char **pp, int size) {
int i;
//Commenting following curproc for lab3 as it is not needed.
//struct proc *curproc = myproc();
if(argint(n, &i) < 0)
return -1;
//Commenting following 'if' for lab3 and added another 'if' containing
//STACKBASE
//if(size < 0 || (uint)i >= curproc->sz || (uint)i+size > curproc->sz)
if(size < 0 || (uint)i > STACKBASE || (uint)i+size > STACKBASE)
return -1;
*pp = (char*)i;
return 0;
}

(四)、编写用户程序 test
递归申请内存:每次申请1k的内存,实验中递归300次,相当于300k左右,超出了一个页面的大小,会触发之前在trap.c中定义好的缺页中断T_PGFLT。
在这里插入图片描述

四、运行结果

输入 test 将输出进程的相关信息。
在这里插入图片描述在这里插入图片描述

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值