目录
Stopping and Continuing(停止并继续)
Source and Machine Code(源代码和机器代码)
连续内存分配(Contiguous Memory Allocation)
动态存储分配(dynamic storage-allocation)
[2]操作系统中的自陷Trap(software interrupt)/异常Exception、中断Interrupt
Translate a logical address to a physical address(将逻辑地址转换为物理地址)
Previously on OS...
存储器的层次结构
register-high speed cache-main memory-fixed disk-removable storage
(寄存器高速缓存—主内存—固定磁盘—可移动存储)
操作系统的内存管理
可执行存储器(寄存器+主存)的分配、回收以及提供在存储层次间数据移动的管理机制。
程序运行的步骤
- compile(编译)【c-obj】
- link(链接) 【modules+objs】
- load(载入) 【allocate memory(分配内存)】
地址绑定
编译时-绝对代码/装入时-可重定位代码/运行时-(base+limit)(基础+限制)
逻辑地址(CPU生成的地址)和物理地址(MMU内存地址寄存器中的地址)
虚拟地址:在程序运行时的地址绑定机制生成的逻辑地址。
Virtual address(虚拟地址)---》MMU --》Physical address(物理地址)
base/relocation register + limit register ----》 address space mapping(地址空间映射)
动态装入(使用时装入) ---》动态链接和共享库(Dynamically linked libraries-动态链接库,DLLs)
Previously on Experiment
内核的编译
gdb调试【1】
Stopping and Continuing(停止并继续)
break(中断)/continue(继续)/finish(完成)/step(步骤)
Examining the Stack(检查堆栈)
backtrace(回溯)/info register(信息寄存器)/info stack(信息栈)/info frame(信息帧)
Source and Machine Code(源代码和机器代码)
disassemble(拆卸)/info line(信息线)
中断机制
Now...
连续内存分配(Contiguous Memory Allocation)
- 主存要保证操作系统和用户进程的使用。通常分为两个分区:分别供操作系统和用户进程使用。
- 操作系统使用高位内存地址(high memory addresses)还是低位内存地址(low memory addresses)取决于很多因素,比如中断向量(interrupt vector)的位置。
- Windows和Linux使用高位内存地址。
- 在连续内存分配策略中,每个进程包含在单一的内存段中(single section of memory),多个进程的内存段是连续的。
- 如何使各进程拥有独立空间?(保护内存)
- 大部分时候内存中同时驻留多个用户进程,如何为即将装入内存的进程分配可用内存?(分配内存)
内存保护机制(Memory Protection)
- 防止进程访问不属于它的内存空间。limit register + relocation register
- 当CPU访问逻辑地址时,先与界限寄存器的地址比较,如果超出许可的界限,则触发系统自陷【2】。如果是合法地址,则与重定位寄存器的地址相加,得到真实的物理地址。
- 当CPU调度器(CPu - Scheduler) 准备挑选一个进程进入运行状态时,分派器(dispatcher)也载入了重定位寄存器和界限寄存器的内容,作为上下文切换的一部分【3】。
- 在进程访问内存地址时,我们可以比较CPU生成的逻辑地址与界限寄存器的内容,从而避免内存的非法访问。
内存分配机制(Memory Allocation)
- 将进程指派到内存中的可变大小的分区,每个分区只包含一个进程。可变分区(variable - partition)方案下,OS维护了一张数据表,记录了哪些内存是可用的,被看作一大块可用的孔(hole)。
- 正常情况:当进程载入系统时,OS评估每个进程请求的内存和可用内存,决定分配给谁、分配多少内存。当进程获得了空间,进程得以进入内存并运行。当进程结束时,释放占用的内存,并给其他进程使用。
- 如果没有足够的空间分配给进程呢?
- 拒绝进程执行,返回错误信息。
- 将进程放入等待队列,后续有进程释放内存后,OS检查等待队列,再为队列中的进程分配空间。
- 通常,可用内存块由一个孔集合(a set of holes)组成,孔的大小不一,分布在整个内存中。当进程需要内存是,系统查找孔集合中是否有合适的孔。如果一个孔太大,可以将它分为两个部分,其中一个孔提供给进程,另一个孔重新放入孔集合中。
- 这个过程是动态存储分配问题(dynamic storage-allocation problem)的一个特例,即根据一组空闲孔来分配大小为n的请求。可以使用首次适应(first-fit),最佳适应(best-fit),最差适应(worst-fit)等算法。
动态存储分配(dynamic storage-allocation)
首次适应(first fit):分配第一-次找到的能。满足要求孔。检索孔列表,可以从头开始,或者从上次的FF搜索结束的位置开始。找到第一一个符合要求的孔时,结束检索。
最佳适应(best fit):分配能满足要求的最小的孔。如果孔列表没有排序,则必须检索整个孔列表。
最差适应(worst fit):分配最大的孔。如果孔列表没有排序,则必须检索整个孔列表。总是提供剩余孔中,最大的孔。
动态分区分配中的数据结构
(1) 空闲分区表:每个空闲分区占一个表目,表目中包括分区号、分区大小和分区起始地址等;
(2) 空闲分区链:实现对空闲分区的分配和链接,在每个分区的起始位置设置一些分配的信息、链接各分区的前向指针,分区尾部设置后向指针。尾部也可以重复设置状态位和分区大小表目。状态位为1时表示分区已分配,0表示空闲。
碎片问题(Fragmentation Problem)
- 首次适应和最佳适应都有外部碎片(external fragmentation)问题。随着进程加载到内存或从内存中移除,空闲内存空间被分为小的片段。当总的可用内存之和可以满足请求,但空间并不连续时,就会导致外部碎片问题:存储被分成了大量的小孔。 '
- 在最坏情况下,每两个进程之间就有空闲(或浪费的)块。如果这些内存是一整块,那么可以再运行多个进程。
- 不管是first-fit还是best-fit都会引起碎片总量的增加。另外一个因素是从空闲分区的哪一端分配。
- 碎片也可以是内部的。假设一个孔有18464字节,而进程只需要18462字节。如果按需求分配,剩余2字节。记录2字节孔的开销比孔自身大很多。
- 解决办法:将物理内存划分为固定大小的块,根据块大小分配内存单元。此时为进程分配的内存略大于进程请求的大小。
- 分配的大小与请求大小之差称为内部碎片(internal fragmentation),即在分区内部未使用的内存。
- 解决外部碎片的办法之一:紧缩(compaction)/紧凑。通过整理内存中的内容,使可用内存集中到一起。
- 如果重定位是静态的,并且在汇编时或加载时进行的,不能使用紧缩。只用重定位时动态的,并且是在运行时进行的,才能使用紧缩。如果地址被动态重定位,可以首先移动程序和数据,然后再根据新的基地址的值来改变基址寄存器。如果使用紧缩,还要评估开销。最简单的合并算法是将所有进程移到内存的一端,将所有的孔移到内存的另一端。
- 另一种解决外部碎片的办法:允许逻辑地址空间不连续,允许进程被分配到任何可用的物理地址之上。
- 这种策略称为分页(paging),大多数计算机系统使用此类策略。
- 当需要管理数据块时,碎片问题都会发生。
分页(Paging)
- 避免了外部碎片和紧缩。在服务器/大型机和移动设备中广泛使用。
- 通过OS和硬件的协作来实现分页机制。
- 基本方法:把 物理内存 分为固定大小的块,称为帧/页帧(frames),把 逻辑内存 分为同等大小的块,称为页/页面(pages)。
- 当需要执行一个进程时,它的页从文件系统或者备份存储等源处,载入到任何可用的内存帧之内。这样,逻辑地址空间与物理地址空间完全分离,即使物理内存小于2^64 ,进程在逻辑上仍然可以使用2^64的地址空间。
- 由CPU生成的每个地址(逻辑地址)分为两部分:页号p(page number)和页偏移d(page offset。页号作为页表(page table)的索引。
- 注意:教材中使用页号P和位偏移量W。
logical address --> physical address
page number--> frame address(page table)
frame address + page offset --> physical address
- 页表包含了物理内存中每个帧的基础地址,偏移量指相关帧里的位移/位置。
- 所以,物理地址是由基础地址和页偏移量来定义的。
- MMU将CPU生成的逻辑地址转换成物理地址的步骤:
- 1. 将页号p提取出来,作为页表的索引;
- 2. 将页表中对应的帧号f提取出来;
- 3. 使用帧号替换掉逻辑地址中的页号。
- 由于偏移量d没有发生变化,无需替换,帧号和偏移量组成了物理地址 。
- 页的大小是2的幂,是由硬件定义的,在不同的计算机架构上,每页的大小可以从
4KB到1GB。 - 如果逻辑地址空间是2m,页大小是2n字节,那么高阶m-n位的数字指定页号,n个低位数字指定页偏移量。逻辑地址如下如所示:
- 逻辑地址地址空间大小为:24,页大小为22,即:n=2, m=4。使用4字节的页,32字节(8个页)的物理内存。
- 逻辑地址0是页0,偏移量0。从页表可知,页0的帧地址是5,所以逻辑地址0映射为物理地址是20【=5*4+0】
- 逻辑地址3的物理地址为多少?23=5*4+3
- 逻辑地址4的物理地址为多少?24=6*4+0
- 维护页记录需要开销。页越大开销越低。
- 典型的页大小为4KB或8KB。Windows 10支持从4KB到2MB的页大小。
- 当系统准备执行进程时,按页检查进程的大小。进程的每一页都需要一个帧。如果进程需要n页,那么内存中至少要有n个帧。每个页号及其对应的帧号,都被记录到页表中。
-
分配内存前后 的空闲帧
- 分页使得程序员把内存看作单一的空间。而实际上,用户程序被离散的分布在物理内存中;而且内存中可以同时存在多个用户程序。地址映射是由操作系统控制的,对程序员不可见。
- 操作系统记录了物理内存的分配细节,哪些帧已分配,哪些帧已被使用,总共有多少帧等等。这些信息保存在帧表(frame table)数据结构中。对于每个物理页面帧,都有一个入口,指明了该帧是空闲或被哪个/哪些进程所占有。
- 操作系统不仅维护指令计数器和寄存器的内容,也为每个进程维护一个页表的副本,用作将逻辑地址转换为物理地址。当一个进程分配到CPU时,分派器也根据该副本来定义硬件页表。因此,分页增加了上下文切换的时间开销。
参考
[1]GDB调试
-
当程序调用某个函数时,会产生关于该调用的信息。包括程序调用的地址,调用的参数,局部变量等。这些信息保存在一个数据块,称为栈帧Stack Frame。这些栈帧在一个内存区域,称为调用栈Call Stack。
-
当程序停止时,GDB提供了一系列的命令查看栈的信息。如backtrace、info stack、info frame可以查看当前执行的栈、栈帧信息。
[2]操作系统中的自陷Trap(software interrupt)/异常Exception、中断Interrupt
-
在运行进程时,CPU的执行过程为:读取指令,更新程序计数器,执行指令,重复以上步骤。但是有时侯下一个指令需要通过内核来执行,即将用户程序的控制权转交给内核,以提升指令的执行权限。
-
自陷通常是一个由用户进程引起的异常(Exception),是导致CPU指令不能继续执行的异常,是处理机指令内部的事件,比如除0、访问无效内存地址等。或用户进程在执行系统调用也可以引发陷阱。自陷是一种调用系统功能/执行权限提升的常见手段。当进程执行自陷时,由自陷指令(trap instruction)保存用户代码的状态,切换到内核态,然后分派给对应的内核功能并进行处理,如执行系统调用或服务等。自陷也称为软件中断。异常不能被屏蔽,一旦发生应立即处理。
-
中断:也称外中断。指来自处理机执行指令以外的事件发生。由软件或硬件发出的信号。硬件和软件都产生这些信号,被称为硬件和软件中断。I/O结束中断、时钟中断。不同的中断具有不同的优先级。在处理高级中断时,低级中断可以被临时屏蔽。中断是异步发生的,并且可能随时发生
[3]调度器Scheduler和分派器Dispatcher
调度器:决定让哪个进程使用CPU。
长程调度/作业调度:决定多少进程留在主存,剩下的留在外存。
中程调度:当进程在运行时需要I/O(运行阻塞),或I/O完成需要CPU时(阻塞就绪),由中程调度完成切换。
短程调度:CPU调度器。从就绪队列中选择进程以分配CPU。
分派器:调度器完成工作之后,由分派器执行以下工作:修改进程的状态或进程所在的队列。包括:
切换上下文;
切换到用户模式;
跳转到合适的地址(恢复点)以继续执行程序。
-
由于涉及到上下文切换,分派器要尽可能快速地执行。分派器的工作时间称为分派延迟(dispatch latency)。
Translate a logical address to a physical address(将逻辑地址转换为物理地址)
The following outlines the steps taken by the MMU to translate a logical address generated by the CPU to a physical address:
1. Extract the page number p and use it as an index into the page table.
2. Extract the corresponding frame number f from the page table.
3. Replace the page number p in the logical address with the frame number f .
As the offset d does not change, it is not replaced, and the frame number and offset now comprise the physical address.
下面概述了 MMU 将 CPU 生成的逻辑地址转换为物理地址所执行的步骤:
1. 提取页码 p 并将其用作页表中的索引。
2. 从页表中抽取相应的帧编号 f。
3. 将逻辑地址中的页码 p 替换为帧号 f 。
由于偏移量 d 不会更改,因此不会替换它,并且帧编号和偏移量现在包含物理地址。