跳到内容

操作系统与存储器管理

Canyue
发布日期:
11 分钟阅读

以下内容涉及知识点较深,可能有误,望指出。参考时建议再次查证内容的准确性。

本篇是以OS的视角,研究分页和分段存储优先看这篇

冯诺依曼型计算机是由运算器、存储器、控制器、输入设备和输出设备五大部件组成,现存计算机都遵循该设计

存储器是一种用于存储程序和数据信息的部件,是一种时序电路

存储器的层次结构

在计算机执行指令时,基本上都会涉及到存储器的访问,故存储器的性能影响到计算机的运行效率。

但如今随着计算机技术的发展,对存储器的容量需求也越来越大,通常一种存储器无法同时满足快速与大容量甚至低廉的成本,故现在计算机均采用多层次的存储器设计。

对于通用计算机,通常将存储器分为三个层次,与中央处理器(CPU)的距离由近到远分为:

存储技术

这里简单介绍些常见的,可快速带过

随机访问存储器

也被称为随机存取存储器(RAM),被分为两类:SRAM(Static Random Access Memory,静态随机存储器)与DRAM(Dynamic Random Access Memory,动态随机存储器),SRAM更快,成本也相对更高。

RAM工作时(刷新时除外)可以随时从任何一个指定的地址写入(存入)或读出(取出)信息

RAM的读写速度很快,但存储其中的数据是易失的,这就意味着一旦断电存储的说有数据将丢失,故常被用作临时的存储介质

SRAM

SRAM通常采用6管MOS制成,这种电路结构天生在通电时具有双稳态性(也就是图中,要么1,要么3,在其他状态都是不稳定的,会很快的恢复到一个稳定状态下)

正是由于双稳态性,SRAM无需刷新即可保持在稳定值,即使受到干扰,在干扰消除时也可很快恢复,这些特点使其特别适用于需要高速访问和可靠存储数据的场景,如CPU与主存之间的高速缓存、CPU内部的L1/L2等

但由于SRAM集成密度小、芯片面积大、成本高,现如今DRAM仍是主流

DRAM

DRAM采用电容对数据位进行存储,正是由于采用电容来存储bit数据,由于存储在电容器上的电荷会随着时间的推移而泄漏,因此DRAM需要定期刷新以确保数据的完整性

DRAM存储器对干扰非常敏感,若电容的电压被扰乱就永远无法恢复

DRAM相较于SRAM拥有更高的密度,这也意味着成本更低,但速度要比SRAM慢

非易失性存储器

非易失性存储器:即即使在断电后,仍能保持器数据的存储器

ROM

只读存储器(Read-Only Memory,ROM),原本是指一种以非破坏性读出方式工作,只能读出无法写入信息的存储器,但随着技术的发展,现如今部分ROM可以实现读写

在计算机主板PCB上封装着一块ROM,用于存放固件(firmware),这块存储器也属于内存的一种,也可由CPU直接寻址,同样例如显卡、磁盘驱动器等复杂的设备,也需要由固件翻译来自CPU和I/O设备的请求

PROM

可编程只读存储器(Programmable ROM,PROM)允许用户通过专用的设备(编程器)一次性写入自己所需要的信息,这类存储器只可被编程一次,被编写的数据会被永久性保存。

PROM的每个单元时一种融丝,在出厂时,PROM中的数据全为1/0,由用户使用时,再通过编程使PROM存储需要的数据,此时熔丝熔断,数据被写入,这也是只能被编程一次的原因

EPROM

可编程可擦除只读存储器(Erasable Programmable Read Only Memory,EPROM)可多次编程,是一种以读为主的可写可读的存储器,在写入新数据时,需要把原先的内容先擦除才能写入。

EPROM会留有一个透明的石英窗口,当紫外线透过窗口照射在EPROM单元上,该单元会被重置为0。故EPROM需要使用专用设备才能进行擦写。

EEPROM

电可擦可编程序只读存储器(Electrically Erasable Programmable Read-Only Memory,EEPROM)是一种可以随时写入而无需先擦除原先内容的存储器,在写入时,也无需使用专用设备。

EEPROM的写入往往要比读取的速度慢

Flash

快擦除读写存储器( Flash Memory)是一种高密度、非易失性的读/写半导体存储器。

其擦写速度要比EERPOM快得多,目前,闪存已广泛用于制作各种移动存储器,如U盘及数码相机/摄像机所用的存储卡等。

程序的装入与链接

要在系统中运行用户程序,就必须将其由外存装入到内存中,并将其转化为一个可执行的程序,

在此过程中,会经历以下三步:

地址绑定

以CPU的视角,生成的地址通常为逻辑地址(Logic Address),也叫相对地址

以内存单元的视角,能看到的地址为物理地址(Physical Address),也叫绝对地址

在编译和装入的过程中,地址绑定会产生相同的逻辑地址和物理地址

在执行时,地址绑定会生成不同的逻辑地址和物理地址,此时成逻辑地址为虚拟地址(Virtual Address)

由程序所生成的所有逻辑地址集合被称为逻辑地址空间(Virtual Address Space)

由这些逻辑地址所对于的物理地址的集合被称为物理地址空间(Physical Address Space)

内存保护

内存保护是操作系统对电脑上的内存进行访问权限管理的一个机制。其主要目的是防止某个进程去访问不是操作系统配置给它的寻址空间,从而防止该进程因某些程序错误或问题而有意或无意地影响到其他进程或是操作系统本身的运行状态和数据,保证进程之间不会相互影响。

这种保证通常时基于硬件实现的,因为一旦OS进行干预就会严重影响性能

编译

一种由编译器(一种程序)将用户程序进行处理,并转换为若干模块的过程,这一过程就是编译

链接

由链接程序将编译后产生的一组目标模块以及所需的库函数链接到一起,形成一个完整的装入模块的过程

根据进行装配的时机不同,分为一下三种

静态链接

在程序运行前,先将各目标模块以及其所需函数库链接成一个完整的装配模块,以后不再拆开,

静态链接有以下特点:

在模块装配时,需解决以下问题:

设有三个模块A、B、C,长度分别为L、M、N,A模块调用B模块;B模块调用C模块

这部分内容与书本有出入,待核实!!

动态链接

动态链接则是不一口气将模块装入,而是按需装入

具有以下特点:

装入时动态链接

将用户源程序编译后得到的一组目标模块,在装入内存时,采用边装入边链接的方式。

当发生一个外部调用事件,则让装入程序去找到被调用的外部目标模块,并装入。

运行时动态链接

对某些目标模块的链接,是在程序执行中需要该模块时才进行的。其优点是便于修改和更新,便于实现对目标模块的共享

装入

也被称为加载:由装入程序 将装入模块 装入内存的过程

绝对装入方式

当计算机系统很小,且仅能运行单道程序时(即:所有进程一个一个排对执行。若A阻塞,B只能等待的一种程序设计),此时用户程序在编译后产生的目标模块会带有一个目标代码,代码中包含模块所要装载的位置,这个位置是个内存中的绝对地址。也可以理解为,此时编译产生的目标模块内,逻辑地址=物理地址,程序中的所有内存引用都是基于这个绝对地址。

绝对装入方式仅适用于单道程序环境,因为在多道程序环境中,编译器无法预知编译后的程序该放在内存中的何处

可重定向装入方式

装入时对目标程序中指令和数据的修改过程称为重定位,因此可以将模块装载在内存的”任何位置”,地址变换通常实在装入时一次完成的,所以又称静态重定位

由于是一次性完成,故在装入内存时,需为装入模块分配全部的内存空间,这段空间应该是连续的。

若此时系统内存剩余容量不足以装载模块时,模块将不会被装载

若容量足够,但无连续空间装载模块时,OS可能会尝试移动其他模块,以空出合适的空间装载模块。

本方式在装载模块后不允许程序运行时在内存中移动位置,此时对模块的移动会影响模块中数据地址发生错误。

动态运行时装入方式

装入程序把装入模块装入内存后,并不立即把装入模块中的相对地址转化为绝对地址,而是把这种地址转换推迟到程序真正要执行时才进行。因此装入内存后的所有地址均为逻辑地址,这种方式需要一个重定位寄存器的支持。

故这种装入方式,允许程序在内存中发生移动,且在程序运行前只装载部分代码,后续再陆续按需动态分配内存,故这种装入方式可将程序分配在不连续的内存中。

存储分配方式

为使用户程序装入内存,则必须为其分配一定大小的存储空间

连续分配存储方式(分区)

简称连续分配方式,被广泛用于20世纪60-80年代的OS

这种分配方式为用户程序只分配一段连续的内存空间,用户程序中的代码或数据被放置在连续的内存空间

单一连续分配

在早期单道批处理系统的小型机中,内存常被分为系统内存区用户内存区,系统内存区被分配至低位,而高位的其他空间,也就是用户内存区中仅有一道用户程序,故整个用户内存区有一个用户程序独占,而这种存储分配方式称为单一内存分配。

正因如此,早期部分的单用户单任务的OS,并未采取存储器保护机制,这是因为单用户环境不存在用户之间互相干扰的问题,并且若OS部分内存遭受破坏,课程通过重启重新载入,相对影响较小,而不采取保护机制,可以节约硬件资源

固定连续分配

20世纪60年代出现了多道程序系统,为使得内存可以装入多道程序,且这些程序之间不会互相干扰,于是将用户用户内存区的空间划分为多个大小固定的区域,这些区域也被称为分区,每个分区只装入一个作业。这也是最早的、最简单的一种可以运行多道程序的分区式存储方式。

此时加入用户空间中有4个分区,那么现在便能允许4个程序并发运行并且互不干扰。当一个作业结束时,分区将空出,并从外存中后备作业列队中选择一个合适大小的作业装入。

虽然这种分配方式实现简单,但可能会出现当用户程序过于大,可能所有分区都无法满足需求,此时还需使用覆盖技术解决,影响性能

且分区技术虽不会产生外部碎片,但会产生内部碎片。

分区的划分

分区的大小可以时相等或是不相等的

内存的分配

为方便内存分配,OS常将分区按照其大小进行排队,并建立一张分区表。

表通常使用数组或链表实现,记录有分区大小、起始地址、是否已被分配状态等信息。

动态分区分配

动态分区分配属于可变分区分配,它是不会事先划分内存分区,而是根据实际需求动态的对为用户程序分配合适的内存空间

使用数据结构记录使用情况

为实现动态分区分配,OS必须配置相应的数据结构,以描述分区的分配情况,进而为分区的分配提供依据。

以这张内存分配情况示意图举例,常用的数据结构有以下两种:

动态分区分配算法

若多个分区都满足需求,那作业应该装入到那个分区,这就有相应的动态分区分配算法决定。

由于分配算法往往对系统性能会产生较大影响,故人们发明了很多中算法,如下:

基于顺序搜索
基于索引搜索

讲人话就是,作业来了,会尝试将内存不停的对半分,直到得到一个最小的能装的下专业的内存空间

由于是对半分,这两半就是一对伙伴

当两个伙伴都空闲时,就会合并回去

可变分区的分配与回收

可变分区的分配

设请求的分区大小为u.size,空闲分区表中每个空闲分区大小为m.size,事先规定的最小不可切分大小为size

可变分区的回收

系统在回收分区时,会从当前链表中找到相应的插入点用于插入空闲分区信息,此时就存在以下3种情况

动态重定向分配

在连续分配存储方式中,随着计算机的运行,计算机内存中将产生很多小而零散的分区,这些小分区难以被利用,这些分区也就是外碎片

怎么解决

紧凑是将内存中的作业进行移动,将它们移动到一起,以腾出一块大的空闲空间的方法

紧凑有时也被称作:紧缩、拼接

由于紧凑操作对内存中的程序和数据进行移动,这就导致位置发生改变,此时就需要对移动后的程序和数据进行重定位

OS会在一段时间对内存进行一次紧缩和重定位操作,以确保系统的内存利用率,但这些操作比较复杂,其实对系统性能影响很大,过于频繁的操作可能会严重影响系统的性能

分页存储方式

连续分配方式在计算机的运行过程中会产生很多碎片,虽然可以提供紧凑解决,但对系统性能开销较大,如果允许单个作业零散的被分配到不相邻的区域中,就能更加有效的利用内存空间

分页存储方式中,将用户程序地址空间划分为若干各大小固定的区域,这些区域就是也叫页面

每个页都有对于的编号,也就是页号,从0开始编号

每个页在物理内存中就是一段段连续的空间块,这些空间被叫做页框,同样页框也会从0开始编号,也就是页框号,页框号和页号是两个东西 $$ 页框号 = INT[逻辑地址 / 单个页框长度] $$ 页框的在物理地址中的起始位置就是页基址,在页框大小相同时 $$ 页基址 = 页框号 * 页框大小 $$ 程序/数据在页框中的位置,基于页基址的偏移量页内偏移量,也叫页内地址 $$ 物理地址 = 页基址 + 页内偏移量 $$ 更多细节可以参考这个

页面大小与内碎片

页面的大小不是越大/越小就越好

由于一个作业常常占不满分配给该作业的最后一个页,这就势必会产生内碎片

通过设置更小的页可以提高内存的利用率,但更小的页也将会使一个作业分配到更多的页,这会使该进程的页表过长,不仅占用内存,也会降低页面换入/换出的效率

分页地址结构

以下只考虑纯分页管理的情况,不涉及段页式混合管理,且只考虑X86架构

分页地址由 低位的12位页内偏移量 和处于 高位的20位的页号组成

页表

在分页系统中,各进程的各页可能零散的存放于物理内存空间中,位确保进程能够顺利运行,系统将为每个进程新建页面映射表,简称页表

页表由操作系统进行维护,一般程序无权访问与修改,操作系统会定期维护页表

在进程地址空间中的所有页,都依次在页表中由一个页表项,页表中存放着页基址等信息,实现页号到物理地址(页框)之间的映射

分页地址的变址

为使用户地址空间的逻辑地址转换为内存空间的物理地址,系统中必须设置地址的变换机构

基于地址的变址机构

变址操作的频率非常高,故变址机构会利用硬件进行实现

例如页表就是利用一组专门的寄存器实现

由于寄存器制造成本高,故大多数页表存储在内存中

在索引时,先将页号与页表长度进行比较,若大于页表长度,也就是越界,此时就会产生一个越界中断

若未越界,页号将在页表中得到页基址,将页基址对应的物理块号存放于物理地址寄存器中

再加上逻辑地址中的页内偏移量就得到数据的物理地址

在索引时,先将页号与页表长度进行比较,若大于页表长度,也就是越界,此时就会产生一个越界中断

若未越界,页号将在页表中得到页基址,将页基址对应的物理块号存放于物理地址寄存器中

再加上逻辑地址中的页内偏移量就得到数据的物理地址

具有快表的地址变换机构

由于页表被存放于内存中,这就意味着当CPU每次存取数据时,就需要访问内存两次,着就降低了系统的运行效率

为提高变址效率,再变址机构中设立一个具有并行查能力的高速缓冲寄存器,也就是联想寄存器,也就是快表(TLB, translation lookaside buffer)

TLB页表不受操作系统维护,而是交由MMU硬件。MMU会不间断更改TLB页表的内容,例如在TLB miss(TLB 未命中)时,MMU会将未命中的页表项插入TLB页表。

在进行变址时,会优先去TBL中查找,若未命中,才去内存中找页表查找

引入快表后的内存有效访问时间

从进程法术指定逻辑地址的访问请求,经过变址。再到内存中找到对应的物理地址,这一流程所花费的时间被称为有效访问时间(Effective access time, EAT)

计算公式如下: $$ EAT = a λ +(t + λ)(1 - a) + t \ = 2t + λ - t a $$ λ表示查找快表所需要的时间 | a表示命中率 | t表示访问一次内存所需要的时间

多级页表

以下只考虑纯分页管理的情况,不涉及段页式混合管理,且只考虑X86架构

页表存储虽然可以使一个作业分配在若干个零散的内存空间中,但页表必须连续存放,当一个页表很大时,就需要占用一段很大的连续空间。且链表必须常驻内存,大量表也会造成内存浪费

我们可以将链表进行分组,使一个内存块正好放下一个页表(这里以一个内存块4KB为例)

对离散的页表专门分配一张页表,这张页表就是外页表,也叫页目录/顶层页表

为方便实现地址转换,在变址机构中,需要增设一个外层页表寄存器,用于存放外城页表的起始位置

外页表中的每个页表向记录着各页表的物理块号,如下

当页面大小为4KB(12位)时,若采用一级页表结构则应具有20位(32-12)的页号,即此时页表项有1M个

当采用两级页表结构时,假设每个页表项占用4B,则每个页可包含2^10个页表项,最多有2^10个分页。

在64位环境下

对于32位计算机,采用二级页表结构很合适

但对于64位计算机就不一定,例如此时页面大小仍是4KB,即2^12B,每个页占用4B,地址还剩下52位(64-12),假定物理块大小还是4KB,则还剩下42位(64-12-10)用于外层页号,也就是外层页表凯南有4096G个页表项,将占用4096GB的空间,很显然是不可接受的

此时必须将现在的外层页表在分成两层,也就是三层页表的结构

分段存储方式

分页存储主要是为了提高内存利用率,而分段存储主要目的则是为了更好地满足用户的逻辑需求,如数据共享、数据保护和动态链接等。因此,当今许多高级语言支持也使用分段存储方式。

分页存储是以进程为单位分配连续的空间,而分段存储将以分段为单位分配连续空间,而一个进程可能由多个分段组成

分段管理的诞生,最早是为了扩充8086处理器的寻址空间另一篇文字细说

分段

在分段管理方式中,作业地址空间被分为若干段,并用段号表示不同段

每个段都从0开始编址,并采用一段连续的地址空间,且各个段之间彼此独立

每个段都具有自己的段基址(段在物理地址中的起始位置)以及长度

每个段可以定义一组相对完整的逻辑信息,如主程序段、子程序段、数据段等

段的长度不固定,而是由程序自身逻辑关系进行划分,一个进程可划分为多个段

分段地址由地位的段内地址以及高位的段号组成

段表

由于分段存储是将一个作业空间按照程序自身逻辑关系,划分为若干大小不等的段,这些段可以零散的存储在内存用户区或是分页中(段页结合管理方式)

由于段的位置不好确定,OS会维护一张段号和段基址之间对应关系的映射表,也就是段表。

段表中的每个项表示一个段,记录了段号、段基址、段长度等信息

段表可放置于寄存器中,以提高运行效率

分段地址的变址

系统中通常会设置段表寄存器,段表寄存器中存储了段表的起始位置以及段表的长度(TL)

当分段地址需要变址时,会先将段号与段表起始位置进行累加,再与段表长度TL进行比较

若大于段表长度,也就越界了,此时将发出中断信号

若小于,则会尝试在段表中查找段表项,得到段基址于段长

此时利用累加器对段基址于段内偏移量进行相加

此时若大于段长,也是越界、发出中断

若小于则可得到段起始位置的物理地址,编制结束

可以看出,和分页存储利用页表进行变址一样,访问一次数据,就需要读取两次内存

所以,分段存储也会使用“快表”对段表进行存储,和分页类似,就不多赘述了

一维和二维的问题

主要是怎么理解分页是一维的,而分段是二维的这一说法。

发现在网上有争论,我这里结合书中的讲解,赞成‘wfs’的说法

分页完全是系统行为,分页大小可预见,程序员只需利用一个页号就可以表示一个地址,这是一维

而分段是用户行为,分段大小由程序逻辑决定,故大小不好遇见,所以程序员既要给出段号,还要给出段内地址,这是二维


参考资料:

《计算机操作系统(慕课版)》 - 中国通信出版集团 人民邮电出版社

《深入理解计算机系统》 - 机械工业出版社

存储器_百度百科 (baidu.com)

寄存器_百度百科 (baidu.com)

主存储器_百度百科 (baidu.com)

外存储器_百度百科 (baidu.com)

随机存取存储器_百度百科 (baidu.com)

SRAM存储器_百度百科 (baidu.com)

动态随机存取存储器_百度百科 (baidu.com)

只读存储器_百度百科 (baidu.com)

操作系统-程序的装入和链接_怎么区分链接和装入-CSDN博客

单一连续分区分配方式_百度百科 (baidu.com)

连续分配管理方式(单一连续分配 固定分区分配 动态分区分配)-CSDN博客

动态分区分配算法(1、首次适应算法 2、最佳适应算法 3、最坏适应算法 4、邻近适应算法)-CSDN博客

Hash(散列函数)_百度百科 (baidu.com)

分页和分段有什区别? - 掘金 (juejin.cn)

一些有关AI生成的内容

上一篇
Scala解析XML
下一篇
Spark流计算