页表

访问内存的有效时间

主宰稳场 提交于 2019-12-01 08:57:55
从进程发出指定逻辑地址的访问请求,经过地址变换,到在内存中找到对应的实际物理地址单元并取出数据,所需花费的总时间,称为内存的有效访问时间(Effective Access Time, ETA) 在 基本分页存储管理 方式中: 有效访问时间分为第一次访问内存时间(即查找页表对应的页表项所消耗的时间t)与第二次访问内存时间(即将页表项中的物理块号与页内地址拼接成实际物理地址所耗费的时间t)之和:    ETA = t + t = 2t 在 引入快表的分页存储管理 方式中: 通过查询快表可直接得到逻辑页所对应的物理块号,由此拼接形成实际物理地址,减少了一次访问内存,缩短了进程访问内存的有限时间。   ETA = α x λ + ( t + λ )( 1 - α ) + t = 2t + λ - t x α 其中 λ :表示查找快表所需的时间    α :表示查找时的命中率 t :表示访问一次内存所需要的时间 注: 由于快表的容量限制,不可能将一个进程的整个页表全部装入快表,所以在快表中查找到所需表项存在着命中率的问题。所谓命中率,是指使用快表并在其中成功查找到所需表项的比率。 来源: https://www.cnblogs.com/rhgaiymm/p/11674478.html

虚拟地址到物理地址的转换过程

白昼怎懂夜的黑 提交于 2019-11-30 12:24:00
1.arm32 MMU 32位arm MMU支持2级页表,L1和L2(这里不考虑LAPE),支持如下特性 VA--PA,最多2级页表转换 支持32bit的物理地址 支持如下的内存大小 1M或16M的段 4K或64K的页 2.一级页表映射 一级页表的映射过程 图中L1采用1M section映射 TTBA[31:14]表示L1的基地址 一级页表项占4字节,VA[31:20] << 2表示虚拟地址在页表中的偏移 TTBA[31:14] | (VA[31:20] << 2)表示虚拟地址的一级页表项的地址 L1页表项的内容由 PA[31:20] | mmu_flags | [1:0]组成,其中最后2bit表示表的类型 PA[31:20] | VA[19:0]则获得段式映射的PA TTBA从何而来? TTBA保存在CP15 c2中,从图中可以看出其地址是16KB对齐 TTBA地址具体由TTRB0和TTRB1决定,其中TTRB0对应用户空间,TTRB1对应内核空间 L1如何跟VA对应? 每个页表项占4个字节,低2bit为0b00,VA[31:20] <<2即为页表偏移量 每个页表项表示1M的空间,4K个页表项代表4G的进程空间 一级页表项内容如何定义? [1:0]低2位表示页表类型 0b00 ---- invalid 0b01 ---- 页式映射,[31:10]表示二级页表基地址

MySQL的B树索引与索引优化

若如初见. 提交于 2019-11-30 04:18:14
选自:https://www.cnblogs.com/lfs2640666960/p/8550452.html MySQL的MyISAM、InnoDB引擎默认均使用B+树索引(查询时都显示为“BTREE”),本文讨论两个问题: 为什么MySQL等主流数据库选择B+树的索引结构? 如何基于索引结构,理解常见的MySQL索引优化思路? 为什么索引无法全部装入内存 索引结构的选择基于这样一个性质:大数据量时,索引无法全部装入内存。 为什么索引无法全部装入内存?假设使用树结构组织索引,简单估算一下: 假设单个索引节点12B,1000w个数据行,unique索引,则叶子节点共占约100MB,整棵树最多200MB。 假设一行数据占用200B,则数据共占约2G。 假设索引存储在内存中。也就是说,每在物理盘上保存2G的数据,就要占用200MB的内存, 索引:数据的占用比 约为1/10 。1/10的占用比算不算大呢?物理盘比内存廉价的多,以一台内存16G硬盘1T的服务器为例, 如果要存满1T的硬盘,至少需要100G的内存 ,远大于16G。 考虑到一个表上可能有多个索引、联合索引、数据行占用更小等情况,实际的占用比通常大于1/10,某些时候能达到1/3。在基于索引的存储架构中, 索引:数据的占用比 过高,因此,索引无法全部装入内存。 其他结构的问题 由于无法装入内存,则必然依赖磁盘(或SSD)存储

self-mapping page tables

青春壹個敷衍的年華 提交于 2019-11-29 14:18:55
1. x86 10-10-12分页   很久之前就听说过页表的自映射机制,但始终不是很理解。直到昨天读了《Windows内核原理与实现》中内存管理一章才明白了其设计的思想。所以准备记录下来自己的理解。   首先我准备先从最简单的x86两级分页开始(x86 PAE\x64分页准备以后在写),首先x86未开启PAE模式的情况下最大能够索引4GB大小的空间,由于是10-10-12分页,因此PDE有2^10项(1024),每个PDE又能索引2^10项(1024)个PTE,因此描述4GB大小的内存空间总共需要2^20个页表,又因为再此分页模式下每项占4个字节,所以总共需要4MB大小的空间来存放页表。  Windows对页表的存储是在一块连续的4MB物理空间(且地址对齐),Windows将PTE的起始地址放在0xC0000000。由于这4M的页表描述了整个内存空间,而页表也属于这块物理内存的一部分,那么其中就肯定存在从某一项开始连续的1024项页表用来描述这4M的页表自身。如果我们将这1024项页表作为PDE,就节省了4K的PDE空间。实现了页目录表作为页表的复用。这就被称为页表自映射。  每个页表能够描述4KB大小的内存,因此就引出了一个PFN(page frame number)的一个概念,整个内存可以用这个PFN来描述,同时这个PFN又是PTE的索引。PTE_base =

内存管理

佐手、 提交于 2019-11-29 05:40:01
如何进行内存管理 为了让每个进程认为 独占 地使用内存,并且让每个进程看到的内存是 一致 的,操作系统对物理内存、磁盘进行了 抽象 ,抽象出 虚拟内存 。并且把虚拟内存、物理内存以相同固定大小的 页 进行切分管理( 分页 ),虚拟内存中叫页,物理内存中的叫页帧。 每个进程虚拟地址空间是独立的。用户访问的是虚拟内存的地址,即虚拟地址。需要通过 CPU 芯片上的 内存管理单元 MMU 硬件根据页表 翻译 成物理地址,才能真正访问内存。 页表 :每个进程都有它的独立的页表(放在内存里),用来存对虚拟页、物理页的 映射 。页表可以有多级页表,以时间换取空间(实际上,多级页表的地址翻译,并不比单级页表慢很多)。 为什么用分页机制 如果直接按一个个程序加载到内存,会出现内存 碎片 。 后来出现 分段 机制,按程序的各段来存储,从而减少碎片,但是还是有很多。 所以引出分页,把程序分成更小的页(一般大小为 4KB )来管理内存。分得更小,会增加负荷,但实际上利大于弊。 硬件关系 通过虚拟地址 访问 数据: MMU 先通过它里面的 TLB 缓存查询,如果没有,则去内存中的 页表 进行查询。成功翻译成物理地址后,访问 一级缓存 获取数据。如果没有则访问 二级缓存 (可能还有三级缓存)。还是没有就访问 内存 。 物理内存 不够 时: 将不用的页面换出到磁盘中的 swap 分区 里。 内存空间布局

ucore Lab2 实验笔记

风流意气都作罢 提交于 2019-11-29 04:57:51
ucore Lab2 lab 2 直接执行 make qemu-nox 会显示 assert 失败: kernel panic at kern/mm/default_pmm.c:277: assertion failed: (p0 = alloc_page()) == p2 - 1 1 连续物理内存管理 1.1 page 概览 对物理内存的管理,为了节省空间,也是为了配合接下来的虚拟内存管理,通常以某个比 byte 大一些的单位进行管理,我们称这一单位内存为一" 页(page) ",通常是 4KB.待 pages 初始化完毕后,物理内存示意图如下: 其中绿色代表可以分配的内存,红色代表不可被分配的内存.注意, ucore 规定物理内存可用范围最大不超过 KERNSIZE .函数 page_init 的主要作用就是初始化 pages 也就是所有 page 的所有信息. 注意, pages 以全局指针的形式存在,因为最开始无法知道 page 的数量,所以无法写成数量确定的数组.此数量必须尽快确认,否则后期无法管理. 如何确定 page 的数量 npage 呢? 1.1 探测物理内存布局,获取 pages 大小 npages 可由最大物理内存边界/PGSIZE 得出. 而最大物理内存边界可以借助 BIOS 可以探测并计算出来,参考 探测系统物理内存布局 和 实现物理内存探测

我是如何学习写一个操作系统(八):内存管理和段页机制

最后都变了- 提交于 2019-11-29 00:48:30
前言 多进程和内存管理是紧密相连的两个模块,因为运行进程也就是从内存中取指执行,创建进程首先要将程序和数据装入内存。将用户原程序变成可在内存中执行的程序,而这就涉及到了内存管理。 内存的装入 绝对装入。 在编译时,如果知道程序将驻留在内存的某个位置,编译程序将产生绝对地址的目标代码。绝对装入程序按照装入模块的地址,将程序和数据装入内存。装入模块被装入内存后,由于程序中的逻辑地址与实际地址完全相同,故不需对程序和数据的地址进行修改。 可重定位装入。 在多道程序环境下,多个目标模块的起始地址通常都是从0开始,程序中的其他地址都是相对于起始地址的,此时应采用可重定位装入方式。根据内存的当前情况,将装入模块装入到内存的适当位置。装入时对目标程序中指令和数据的修改过程称为重定位,地址变换通常是装入时一次完成,所以成为静态重定位。 其特点是在一个作业装入内存时,必须分配器要求的全部内存空间,如果没有足够的内存,就不能装入,此外一旦作业进入内存后,在整个运行期间,不能在内存中移动,也不能再申请内存空间。 动态运行时装入 也成为动态重定位,程序在内存中如果发生移动,就需要采用动态的装入方式。 动态运行时的装入程序在把装入模块装入内存后,并不立即把装入模块中的相对地址转换为绝对地址,而是把这种地址转换推迟到程序真正要执行时才进行。因此,装入内存后的所有地址均为相对地址

malloc vs memset

纵饮孤独 提交于 2019-11-28 21:51:07
malloc vs memset OS内存分配过程如下: 用户态程序使用malloc接口,分配虚拟地址。 用户程序访问该虚拟地址,比如memset。 硬件(MMU)需要将虚拟地址转换为物理地址。 硬件读取页表。 硬件发现相应的页表项不存在,硬件自动触发缺页异常。 硬件自动跳转到page fault的处理程序(内核实现注册好) 内核中的page fault处理程序执行,在其中分配物理内存,然后修改页表(创建页表项) 异常处理完毕,返回程序用户态,继续执行memset相应的操作。 至此,虚拟内存和物理内存都分配完成,并完成映射。 另一个角度看,如果malloc分配内存后,一直不使用,那就一直不会分配物理内存,这种内存分配策略叫延迟分配 来源: https://www.cnblogs.com/pugang/p/11428245.html

Learn The Architecture Memory Management 译文

瘦欲@ 提交于 2019-11-28 08:53:30
1、概述   本文档介绍了ARMv8-A架构内存管理的关键——内存地址转换,包括虚拟地址(VA)到物理地址(PA)的转换、地址转换表格式以及TLBs(Translation Lookaside Buffers)管理。   对于任何进行底层bootloader或者驱动代码开发的人员来说,这部分内容都是非常实用的,尤其是对进行MMU(Memory Management Unit)编码的人员。   本文档可以帮助你解到VA如何转换成PA的、识别不同的地址空间、地址转换时地址空间是如何映射的以及TLB相关的操作。 2、什么是内存管理?   内存管理描述了如何访问系统内存。每次操作系统或者应用程序尝试访问内存时,都是硬件负责进行内存管理的,对于应用程序而言,内存管理是一种动态分配内存区域的方式。 2.1、为什么需要内存管理?   因为操作系统和应用程序需要大量的内存来运行,同时,应用程序往往运行在虚拟地址空间,需要实际映射到物理地址空间。 3、虚拟地址和物理地址   使用虚拟地址的一个好处是,操作系统可以控制应用程序的内存布局,操作系统决定虚拟地址是否可见、是否允许访问。这种机制允许操作系统采取沙箱机制管理(即应用程序之间的隔离)应用程序,同时实现了对硬件的抽象。   还有一个好处是,物理内存上不连续的内存区域,在虚拟内存地址空间中可以是连续的。   对于应用程序开发人员而言

(三)页式存储管理方案

时光怂恿深爱的人放手 提交于 2019-11-28 02:11:58
页式存储管理方案 位示图 内存被划分成2048块(页)。用32位字长的字存放位示图,为0的位表示该块尚未分配,为1的位表示该块已分配? 实习检查: 1、运行程序,由检查教师给出文件名,该文件中存有内存目前状况的位示图的数据(0和1的文件)。(程序应做提示,界面友好)。 2、你所编制的程序应读入数据,存放在相应的数据结构中。 3、显示友好的用户界面,由检查教师输入内存申请(总块数)。 4、根据申请和位示图状态,为用户分配内存,并建立页表。 5、输出位示图和页表 本程序包括主要实现位示图算法,比较简单. 页式存储管理方案 #include < stdio.h > #include < iostream.h > #include < string .h > const int PAGES = 256 ; // 定义总块数 const int WORD = 32 ; // 定义字长 const int WORDNUM = PAGES / WORD; // 定义总字数 typedef struct node{ char jobname[ 20 ]; int num; int nums[PAGES]; struct node * next; }jobs; int table[WORDNUM][WORD]; int freenum = 0 ; jobs * head; // 初始化函数 void