前言

现在内存管理的方法都是非连续内存管理,也就是结合段机制和分页机制

段机制

段地址空间

  • 进程的段地址空间由多个段组成,比如代码段、堆栈段和符号表段等等

  • 段对应一个连续的内存“块”

  • 不同段在物理内存中是分散的二维结构

段访问

  • 首先由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:物理内存管理的更多相关文章

  1. ucore操作系统学习笔记(二) ucore lab2物理内存管理分析

    一.lab2物理内存管理介绍 操作系统的一个主要职责是管理硬件资源,并向应用程序提供具有良好抽象的接口来使用这些资源. 而内存作为重要的计算机硬件资源,也必然需要被操作系统统一的管理.最初没有操作系统 ...

  2. ucore lab2 物理内存管理 学习笔记

    总的来讲把的LAB1代码逻辑理顺后再往后学就轻松了一大截.LAB2过遍课程视频,再多翻翻实验指导书基本上就没遇到啥大坎儿.对这节学得东西做个总结就是一张图: 练习0:填写已有实验 本实验依赖实验1.请 ...

  3. 《Tsinghua oc mooc》第5~7讲 物理内存管理

    资源 OS2018Spring课程资料首页 uCore OS在线实验指导书 ucore实验基准源代码 MOOC OS习题集 OS课堂练习 Piazza问答平台 暂时无法注册 疑问 段式内存管理中,逻辑 ...

  4. ChCore Lab2 内存管理 实验笔记

    本文为上海交大 ipads 研究所陈海波老师等人所著的<现代操作系统:原理与实现>的课程实验(LAB)的学习笔记的第二篇.所有章节的笔记可在此处查看:chcore | 康宇PL's Blo ...

  5. LInux中的物理内存管理

    2017-02-23 一.伙伴系统 LInux下用伙伴系统管理物理内存页,伙伴系统得益于其良好的算法,一定程度上可以避免外部碎片为何这么说?先回顾下Linux下虚拟地址空间的分布. 在X86架构下,系 ...

  6. Linux内存:物理内存管理概述

    内存中的物理内存管理 概述 一般来说,linux内核一般将处理器的虚拟地址空间划分为2部分.底部比较大的部分用于用户进程,顶部则专用于内核. 在IA-32系统上,地址空间在用户进程和内核之间划分的典型 ...

  7. Lab2 内存管理(实现细节)

    lab2 中的变动 bootloader 的入口发生了改变 bootloader不像lab1那样,直接调用kern_init函数,而是先调用位于lab2/kern/init/entry.S中的kern ...

  8. MIT6.828 Lab2 内存管理

    Lab2 0. 任务介绍 你将编写一个内存管理代码.主要分为两大部分.分别对物理内存和虚拟内存的管理. 对于物理内存,每次分配内存分配器会为你分配4096bytes.也称为一个页(在大部分操作系统中一 ...

  9. Linux下的物理内存管理2-slab缓存的管理

    2017-03-02 在Linux下的物理内存管理中,对SLAB机制大致做了介绍,对SLAB管理结构对象也做了介绍,但是对于小内存块的分配没有介绍,本节重点介绍下slab对小内存块的管理. 内核中使用 ...

随机推荐

  1. SpringCloud 别人的主页

    https://www.cnblogs.com/xuwujing/ java(28) springBoot(14) 设计模式(14) springcloud(7) hadoop(7) hive(5) ...

  2. C# vb .NET从pdf读取识别条形码线性条码

    如何在C#,vb等.NET平台语言里实现快速准确从pdf文件读取,或者从Pdf指定页面读取条形码或QR二维码呢?答案是使用SharpBarcode! SharpBarcode是C#快速高效.准确的条形 ...

  3. 费劲周折的Haskell开发环境搭建过程

    大概倒腾了一周才搭建好Haskell的开发环境,遇到了很多莫名其妙的问题. 首先,Haskell实在是够冷门,中文网站上的信息实在有限.仅有的一些安装教程分享都感觉不大靠谱,所以我还是直接去外网找吧. ...

  4. ASP.NET MVC自定义Module记录管道事件执行顺序

    1. 在Visual Studio 新建项目,模板为空,下面结构选择MVC. 2. 在项目中新建一个类MyModule,实现IHttpModule接口 namespace SimpleApp.Infr ...

  5. EurekaClient自动装配及启动流程解析

    在上篇文章中,我们简单介绍了EurekaServer自动装配及启动流程解析,本篇文章则继续研究EurekaClient的相关代码 老规矩,先看spring.factories文件,其中引入了一个配置类 ...

  6. RabbitMQ高级面试题

    本文涉及:投递失败的消息怎么处理.如何实现延时队列.如何指定消息的优先级.消息的持久化是如何实现的.如何保证消息不丢失 投递失败的消息怎么处理 首先投递失败存在如下两个情况 当交换器无法根据自身的类型 ...

  7. 0 != null 为什么报指针?

    大家好,这是我第一次写博客,来分享我平时工作中遇到的问题及平时学习的技术,如果有写的不好或者不对的地方还望大家能够指出和包涵. 那么接下来就开始说下我工作中遇到的这个问题,我写了一个test,如下: ...

  8. php 读取excel 时间列

    用PHP做一个导入excel功能,发现读取excel时间列的时候总是数据不对,去网上查找了这个函数,转换了一下就好了,真尼玛迷茫了,什么情况,先记录一下,以后再研究吧. 函数如下: function ...

  9. 详解Vue中的虚拟DOM

    摘要: 什么是虚拟DOM? 作者:浪里行舟 Fundebug经授权转载,版权归原作者所有. 前言 Vue.js 2.0引入Virtual DOM,比Vue.js 1.0的初始渲染速度提升了2-4倍,并 ...

  10. IOS—— strong weak retain assign 学习

    转自:http://wenzongliang.iteye.com/blog/1746604 简单讲strong等同retain weak比assign多了一个功能,当对象消失后自动把指针变成nil,好 ...