跳转至

操作系统 | 内存

log
  • 12
  • 13 抽象:地址空间
  • 14 插叙:内存操作API 简单看看
  • 15 机制:地址转换
  • 16 分段
  • 17 空闲空间管理
  • 18 分页:介绍

在虚拟化这一章节

虚拟化内存的目标:

  • 透明:程序本身感受不到内存虚拟化的事实
  • 效率:要在追求虚拟化的同时不消耗太多额外的内存和时间(TLB就是这样的一种硬件功能)
  • 保护:进程间不相互影响

内存操作API(插叙)

malloc()

#include <stdlib.h>

void *malloc(size_t size);

double *d = (double *)malloc(sizeof(double));

free()

int *x = malloc(10 * sizeof(int));
// ...
free(x);

忘记分配内存:

char *src = "hello";
char *dst;
strcpy(dst, src); // segfault and die

上述是c语言的库调用,这些库调用是建立在一些系统调用上的brk, sbrk, mmap()

机制:地址转换

从程序的角度看,地址空间从0开始16KB结束,但对虚拟内存来说,操作系统希望将这个进程的地址空间放在物理内促你的其他位置

怎么在内存中重定位这个进程,同时对该进程透明?

介入

在硬件访问内存的图中插一脚,偷偷修改地址

在虚拟内存中:CPU每次访问内存前,硬件自动把进程给的虚拟地址,转换成真实的物理地址

优点:透明

动态重定位

用基址(base)寄存器和界限(bound)寄存器

为此操作系统产生的新问题

  1. 进程创建时,操作系统要为进程的地址空间找到内存空间
  2. 进程终止是,操作系统回收内存给其他进程或操作系统用
  3. 上下文切换时,每个CPU毕竟只有一个基址寄存器和界限寄存器,所一切换进程是,操作系统要保存和恢复基址和界限寄存器,如放入PCB中

分段

Question

用基址+界限这种方式,会把整个虚拟地址空间完整、连续地放入内存空间,哪怕中间有一大片空白区域进程没有使用

导致了内存浪费

如何解决?怎么支持一个很大的地址空间,但不要把里面没用的空洞也真的放入物理内存?

分段:泛化的基址/界限

segmentation

典型的地址空间里有3个逻辑不同的段:代码、栈和堆,分段的机制可以让操作系统吧不同的段放到不同的物理内存区域,从而避免了虚拟地址空间中的未使用部分占用物理内存

段错误

段错误指在支持分段的机器上发生了非法的内存访问

每个段可以单独放到物理内促你的不同位置,不需要整个地址空间连续放置,这就是泛化的基址/界限机制

也因此,在地址转换时,多了一步:先判断属于哪个段

分段地址转换不是直接:

# VA 虚拟地址
# PA 物理地址
PA = base + VA

而是

  1. 判断 VA 属于哪个段
  2. 算出 VA 在这个段内的偏移 offset
  3. 检查 offset 是否小于该段 bounds
  4. PA = 该段 base + offset

举例

假设虚拟地址空间大小16KB

段表:

虚拟范围 base bounds
code 0KB ~ 2KB 32KB 2KB
heap 4KB ~ 6KB 40KB 2KB
stack 14KB ~ 16KB 28KB 2KB

如果访问VA=5KB

属于heap段,heap段虚拟起点为4KB,所以

offset = 5KB - 4KB = 1KB
检查:1KB < bounds
得到:

PA = heap.base + offset
   = 40KB + 1KB = 41KB

stack有点特殊

heap是向下长,stack是向上长,所以段表里还要表示是正向还是反向增长的信息,除了base/bounds,还可能记录:

权限: read/write/execute
增长方向: positive/neg
是否有效: valid bit

空闲空间管理

在一块连续的堆空间里,如何管理已用空间和空闲空间

free list

链表

申请内存时,就要从空闲空间切分出来(splitting)

申请内存是,如果旁边也是空闲块,就要合并(Coalescing)

分配策略:首次适配、最佳适配、最坏适配

对齐 Alignment

对齐,让访问更高效,代价是产生内部碎片

分页:介绍

虚拟地址 = 页号 + 偏移量

页号 ---页表地址转换->> 物理页号

物理地址 = 物理页号 + 偏移量

页表在哪里

如果没有虚拟内存这个概念,整个地址空间的东西都要放入页表中,页表就很大。

而实际上,内存可以虚拟化,因此页表可以存储在操作系统的虚拟内存中

页表中有什么

要把虚拟地址映射到物理地址

。。。

分页带来的问题

分页新增了一个额外的内存引用,一遍首先从页表中获取地址转换,额外的内存引用开销很大