Lab2:物理内存管理
前言
现在内存管理的方法都是非连续内存管理,也就是结合段机制和分页机制
段机制
段地址空间
进程的段地址空间由多个段组成,比如代码段、堆栈段和符号表段等等
段对应一个连续的内存“块”
不同段在物理内存中是分散的二维结构
段访问
首先由CPU读取逻辑地址,逻辑地址由段号和段内偏移组成
通过段寄存器找到相应的段描述符获得段基址
然后由MMU判断长度是否符合,否则就引发内存异常
最后通过段基址和段内偏移找到真实的物理内存
页机制
页机制把真实的物理内存分为大小相同的基本分配单位,叫做页帧,再接着把逻辑地址空间也划分为大小相同的基本分配单位,页和帧的大小必须是一致的,最后再完成从页面到页帧的映射,也就是逻辑地址到物理地址的转换
页和页帧
页帧和段的寻址方式有点像,也就是当前帧的基地址再加上帧内偏移,当前帧的基地址也是由当前的帧号和每一帧的大小确定的
页的寻址也是相同,但是页号不一定等于帧号,所以也就是在页和帧之间还有一层地址映射
页表
页表就是保存了逻辑地址到物理地址之间的映射关系,逻辑地址中保存着页号和页内偏移,到页表中查询页帧最后得到真实的物理地址
每一个页面都会对应一个页表项,页表会随着进程的变化而变化,页表项不仅记录对应的页帧还保存了一些标志:存在位、修改位和引用位
页机制的性能问题
页机制存储管理机制在访问性能方面可能因为每一次的访问地址需要两次访问而造成低效,并且如果页表过大也会造成性能问题
快表
快表就是类似一个缓存机制,将最近访问的页表项缓存到TLB中,这样每次访问都可以先到快表中查询,如果TLB命中就会节省非常大的时间
多级页表
多级页表就是将页表分为树状结构,通过级级查询找到最后的结果,这样就可以减少每级页表的长度
段页式存储管理
段页式顾名思义也就是结合了段机制和页机制,这样就可以结合段机制在内存保护方面的优势和页机制在内存利用率上面的优势
段页式其实也就是在中间多加了一层映射,由段机制产生的线性地址到页表的映射,这样在逻辑地址中存储着段表和页表的偏移地址,也就是段号和页号,然后通过段表查询到页表的地址,再通过页号查询 到真实的物理内存地址
启用分页机制
kern_entry:
# load pa of boot pgdir
movl $REALLOC(__boot_pgdir), %eax
movl %eax, %cr3
# enable paging
movl %cr0, %eax
orl $(CR0_PE | CR0_PG | CR0_AM | CR0_WP | CR0_NE | CR0_TS | CR0_EM | CR0_MP), %eax
andl $~(CR0_TS | CR0_EM), %eax
movl %eax, %cr0
# update eip
# now, eip = 0x1.....
leal next, %eax
# set eip = KERNBASE + 0x1.....
jmp *%eax
next:
# unmap va 0 ~ 4M, it's temporary mapping
xorl %eax, %eax
movl %eax, __boot_pgdir
# set ebp, esp
movl $0x0, %ebp
# the kernel stack region is from bootstack -- bootstacktop,
# the kernel stack size is KSTACKSIZE (8KB)defined in memlayout.h
movl $bootstacktop, %esp
# now kernel stack is ready , call the first C function
call kern_init
首先要启动分页模式,cr3寄存器就应该存放着一级页表,然后进行分页模式的使能,再做一些准备工作然后跳入kern_init内核初始化
void
pmm_init(void) {
boot_cr3 = PADDR(boot_pgdir);
init_pmm_manager();
page_init();
check_alloc_page();
check_pgdir();
static_assert(KERNBASE % PTSIZE == 0 && KERNTOP % PTSIZE == 0);
boot_pgdir[PDX(VPT)] = PADDR(boot_pgdir) | PTE_P | PTE_W;
boot_map_segment(boot_pgdir, KERNBASE, KMEMSIZE, 0, PTE_W);
gdt_init();
check_boot_pgdir();
print_pgdir();
}
- 先定义物理内存管理的各个函数
- 检测物理内存空间,保留已使用的内存,然后使用pmm-> init_memmap创建空闲页面列表
- 映射物理内存地址到线性地址
- 重新加载全局描述符表
页机制的代码实现
以页为单位来进行内存的分配
struct Page {
int ref;
uint32_t flags;
unsigned int property;
list_entry_t page_link;
};
ref: ref作为页的引用次数
flags: 标识当前页的使用状态,比如如果这个页被内核使用那么它就是保留状态的
property: 标识当前可用的连续块的大小,这个时候这个页是作为这些空闲块的开始地址的
page_link: 空闲块的链表结构
static void
default_init_memmap(struct Page *base, size_t n) {
assert(n > 0);
struct Page *p = base;
for (;p != base + n; p ++) {
assert(PageReserved(p));
p->flags = 0;
SetPageProperty(p);
p->property = 0;
set_page_ref(p, 0);
list_add_before(&free_list, &(p->page_link));
}
nr_free += n;
base->property = n;
}
init_memmap是进行内存的初始化工作,主要就是设置标志位和引用数和加入空闲队列中
static struct Page *
default_alloc_pages(size_t n) {
assert(n > 0);
if (n > nr_free) {
return NULL;
}
list_entry_t *le, *len;
le = &free_list;
while ((le=list_next(le)) != &free_list) {
struct Page *p = le2page(le, page_link);
if (p->property >= n) {
int i;
for (i = 0; i < n; i ++) {
len = list_next(le);
struct Page *pp = le2page(le, page_link);
SetPageReserved(pp);
ClearPageProperty(pp);
list_del(le);//从空闲页链表中删除这个双向链表指针
le = len;
}
if(p->property > n){
(le2page(le, page_link))->property = p->property - n;//如果选中的第一个连续的块大于n,只取其中的大小为n的块
}
ClearPageProperty(p);
SetPageReserved(p);
nr_free -= n;//当前空闲页的数目减n
return p;
}
}
return NULL;
}
alloc_pages是进行以页为单位进行内存的分配,用的是最简单的First-fit算法,也就是从空闲队列中找到第一个可用的块就直接进行分配,然后如果这个块大于需要分配的内存大小,那就进行切割
static void
default_free_pages(struct Page *base, size_t n) {
assert(n > 0);
struct Page *p = base;
for (; p != base + n; p ++) {
assert(!PageReserved(p) && !PageProperty(p));
p->flags = 0;
set_page_ref(p, 0);
}
base->property = n;
SetPageProperty(base);
list_entry_t *le = list_next(&free_list);
while (le != &free_list) {
p = le2page(le, page_link);
le = list_next(le);
// TODO: optimize
if (base + base->property == p) {
base->property += p->property;
ClearPageProperty(p);
list_del(&(p->page_link));
}
else if (p + p->property == base) {
p->property += base->property;
ClearPageProperty(base);
base = p;
list_del(&(p->page_link));
}
}
nr_free += n;
le = list_next(&free_list);
while (le != &free_list) {
p = le2page(le, page_link);
if (base + base->property <= p) {
assert(base + base->property != p);
break;
}
le = list_next(le);
}
list_add_before(le, &(base->page_link));
}
free_pages是alloc_page的逆过程,一样是先设置一些标志位,然后再遍历空闲队列进行空闲块的合并,再重新入队
Lab2:物理内存管理的更多相关文章
- ucore操作系统学习笔记(二) ucore lab2物理内存管理分析
一.lab2物理内存管理介绍 操作系统的一个主要职责是管理硬件资源,并向应用程序提供具有良好抽象的接口来使用这些资源. 而内存作为重要的计算机硬件资源,也必然需要被操作系统统一的管理.最初没有操作系统 ...
- ucore lab2 物理内存管理 学习笔记
总的来讲把的LAB1代码逻辑理顺后再往后学就轻松了一大截.LAB2过遍课程视频,再多翻翻实验指导书基本上就没遇到啥大坎儿.对这节学得东西做个总结就是一张图: 练习0:填写已有实验 本实验依赖实验1.请 ...
- 《Tsinghua oc mooc》第5~7讲 物理内存管理
资源 OS2018Spring课程资料首页 uCore OS在线实验指导书 ucore实验基准源代码 MOOC OS习题集 OS课堂练习 Piazza问答平台 暂时无法注册 疑问 段式内存管理中,逻辑 ...
- ChCore Lab2 内存管理 实验笔记
本文为上海交大 ipads 研究所陈海波老师等人所著的<现代操作系统:原理与实现>的课程实验(LAB)的学习笔记的第二篇.所有章节的笔记可在此处查看:chcore | 康宇PL's Blo ...
- LInux中的物理内存管理
2017-02-23 一.伙伴系统 LInux下用伙伴系统管理物理内存页,伙伴系统得益于其良好的算法,一定程度上可以避免外部碎片为何这么说?先回顾下Linux下虚拟地址空间的分布. 在X86架构下,系 ...
- Linux内存:物理内存管理概述
内存中的物理内存管理 概述 一般来说,linux内核一般将处理器的虚拟地址空间划分为2部分.底部比较大的部分用于用户进程,顶部则专用于内核. 在IA-32系统上,地址空间在用户进程和内核之间划分的典型 ...
- Lab2 内存管理(实现细节)
lab2 中的变动 bootloader 的入口发生了改变 bootloader不像lab1那样,直接调用kern_init函数,而是先调用位于lab2/kern/init/entry.S中的kern ...
- MIT6.828 Lab2 内存管理
Lab2 0. 任务介绍 你将编写一个内存管理代码.主要分为两大部分.分别对物理内存和虚拟内存的管理. 对于物理内存,每次分配内存分配器会为你分配4096bytes.也称为一个页(在大部分操作系统中一 ...
- Linux下的物理内存管理2-slab缓存的管理
2017-03-02 在Linux下的物理内存管理中,对SLAB机制大致做了介绍,对SLAB管理结构对象也做了介绍,但是对于小内存块的分配没有介绍,本节重点介绍下slab对小内存块的管理. 内核中使用 ...
随机推荐
- C#多线程下如何保证线程安全?
多线程编程相对于单线程会出现一个特有的问题,就是线程安全的问题.所谓的线程安全,就是如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码.如果每次运行结果和单线程运行的结果是 ...
- 一次性删除master数据库中的所有用户添加的表
执行查询命令 use master; go sp_msforeachtable @command1="drop table ?" go
- python 函数式编程 闭包,返回一个函数
参考链接:https://www.liaoxuefeng.com/wiki/1016959663602400/1017434209254976 作业 #使用生成器 def createCounter( ...
- javascript(五)表达式
表达式 (expression) JavaScript中的一个短语, JavaScript解释器会将其计算(evaluate) 出一 个结果.程序中的常量是最简单的一类表达式.变量名也是一种简单的表达 ...
- SQL Server强制释放内存
--强制释放内存 CREATE procedure [dbo].ClearMemory as begin --清除所有缓存 DBCC DROPCLEANBUFFERS --打开高级配置 EXEC (' ...
- maven 学习---Maven安装配置
想要安装 Apache Maven 在Windows 系统上, 只需要下载 Maven 的 zip 文件,并将其解压到你想安装的目录,并配置 Windows 环境变量. 所需工具 : JDK 1.8 ...
- 虚拟机配置静态ip
参考地址 https://www.cnblogs.com/maowenqiang/articles/7727910.html TYPE=EthernetBOOTPROTO="static& ...
- docker在linux上的安装
docker安装在liunx环境上,我电脑用的是ubuntu系统的,需要下载对应系统的docker,我下载的是社区版,对着官方的命令敲就好了, 地址是:https://docs.docker.com/ ...
- Maven编译过程中出现的问题
在用Jenkins编译Gitlab上代码过程中,实际使用的是Maven服务器上的打包命令,以下为打包过程中出现的问题及解决方案 问题一:Maven无法编译Snapshot版本代码 答:登录至maven ...
- JfreeChart 乱码问题处理
在前面之间加上下面这段代码即可. //创建主题样式 StandardChartTheme standardChartTheme=new StandardChartTheme("CN" ...