• 操作系统学习笔记(四) 存储模型和虚拟内存


    一、存储器抽象:地址空间

    现代计算机采用多道程序设计,系统中同时运行了多个进程。要保证多个程序同时在内存中并且互不影响,就需要解决两个问题:保护和重定位。

    地址空间:一个进程可以用来寻址内存的一套地址集合。

    每个进程都有自己的一个地址空间,并且这个地址空间独立于其他进程的地址空间。有了地址空间,一个进程就知道了自己可以寻址的范围,从而解决了保护的问题。

    解决了这个问题,又有一个问题出现了:内存不够用。

    现在的PC,一般也就是4G或者8G的内存,甚至可能只有2G。而一个进程,几百M都是很正常的,大型的游戏可能数G了。

    一台电脑要同时运行许多进程,以及一些操作系统自身占用的内存,如果全部程序都在内存中,那么内存是肯定不够用了。

    交换:把一个进程完整调入内存,使该进程进入内存运行一段时间,然后把它存回磁盘。这样,空闲的进程主要存储在磁盘上,不运行就不占用内存。

    交换技术的问题在于,磁盘与内存之间的I/O操作是很慢的,而且会在内存中产生很多空洞(hole)。所以要每隔一段时间进行内存紧缩,这样会花费大量的时间。

    编程语言通常有动态内存的功能,例如C中的malloc,C++中的new和allocator等,一般在堆上进行分配。操作系统需要对动态内存进行管理,方法主要有两种

    (1)位图。位图是一种数据结构,简单来说,就是用一个比特位来表示可用和不可用两种状态。把内存分成大小为N的单元,用一个二进制位来表示可用或者不可用,从而可以用一个比较小的空间表示一大段内存的使用情况。

    位图的缺点是耗时大,查找空闲需要大量的时间。

    (2)空闲链表。操作系统维护一个记录着已分配内存段和空闲内存段的链表。当申请一块新内存的时候,要从空闲链表中进行查找,匹配方式有很多种:首次适配,下一次适配,最佳适配等等。可以对链表进行排序以提高查询的速度。

    此外,还可以对常用大小的内存块分别设置链表,比如4KB的块组成一个链表,8KB块组成一个链表等。这种方式与Linux采用的伙伴算法有一定的相似之处。

    二、虚拟内存

    虚拟内存是大部分现代操作系统所采用的方式。交换方式虽然可以解决一部分内存不够用的问题,但是I/O操作代价是比较大的,磁盘速度比较慢。

    虚拟内存的基本思想,就是每个进程有自己的地址空间,并且把这个空间分成许多块,每一块叫做页面(page)。每一页都是一个连续的地址范围,他们可以被映射到物理内存,页并不需要在进程运行的每一时刻都在内存中,它可以只在需要的时候才被调入,其他时间可以被调出内存。

    进程对应的虚拟地址,构成了虚拟地址空间。虚拟内存可以通过MMU(内存管理单元)映射为物理地址。物理内存中的页称为页框(page frame)。

    实际内存中,用一个标志位表示一个页在不在内存中。如果要读的页不在内存中,将会产生一个缺页中断,使得CPU陷入操作系统中,把页面的内容读到页框中,再次请求该页。

    虚拟内存可以被分为虚拟页号(高位)+偏移量(低位)。

    页表:页号可以作为页表的索引,来找到该虚拟页面对应的页表项。由页表项可以找到页框号,把页框号送到偏移量的高位,形成送往内存的物理地址。物理地址=页框号+偏移量。

    一个页表项需要包括:保护位(允许的访问类型),修改位(是否被修改),‘’在/不在‘’位(是否在内存中),高速缓存允许位,以及最为重要的页框号。当然,还可能有更多位。

    页表的问题在于,如果内存很大,页表项非常多,那么页表占用的内存也会很大。另外,每个进程都要有自己的页表,因为页表提供了虚拟内存到物理内存的转换。

    TLB(快表,转换检测缓冲区):相当于一种缓存,因为有些页面的访问频率很高,所以把这些页面放在一个高速缓存中(例如内置在MMU中)。

    需要访问某个页时,先从TLB中查找,未命中才向页表查找,并用它替换掉另外一个页表项。

    软失效和硬失效:页不在TLB中,但是在内存中,称为软失效;在硬盘中,称为硬失效。很显然,硬盘I/O是很慢的。

    多级页表:页表项太多,会占用太多的内存。于是,多级页表应运而生。本质上,多级页表就是一级索引的项仍然是一个索引,而非页表项。这样可以节约大量的空间,避免把全部页表一直保存在内存中。

    倒排页表:之前的页表是虚拟地址到物理地址的转换。倒排页表,就是物理地址到虚拟地址的映射。这样做的目的,也是为了应对页表太大的问题。这样,每个物理页框有一个表项,记录(进程-虚拟页面)映射到了该页框。倒排页表的缺陷,在于进程使用虚拟地址的时候不方便,必须搜索这个倒排页表才能得到物理页框地址。为了提高速度,可以采用散列的方法。

    三、Linux系统中的实现

    1、基本概念

    Linux操作系统中,每个进程都有三个段:代码段、堆栈段、数据段。通常,代码段不发生变化。

    数据段有两部分:初始化的数据和未初始化的数据(BSS段)。未初始化的数据在加载后被映射到一个专门的静态零页框,赋值后进行写时复制。

    Linux允许数据段随着内存的分配和回收而增长和缩减,从而解决动态分配的问题。

    Linux允许共享代码段内存,但是每个进程的数据和堆栈不共享。

    Linux允许内存映射文件,即把一段内存映射到文件上,文件可以想数组一样被读写。

    2、Linux系统中的内存管理

    Linux区分三种内存区域:

    (1)ZONE_DMA:可以DMA操作的页。

    (2)ZONE_NORMAL:正常规则映射的页。

    (3)ZONE_HIGHMEM:高内存地址的页,不永久性映射。

    Linux的内存由三部分组成:常驻内存的内核和内存映射,以及被划分为页框的其他部分。内核维护一个内存映射,包含所有物理内存使用情况的信息:

    首先,Linux维护一个页描述符数组mem_map,页描述符为page类型,系统中的每个物理页框都有一个页描述符,每个页描述符都有一个指针,指向它所属的地址空间(倒排页表),另外有一对指针可以与其他描述符形成双向链表,记录空闲页框和一些其他域。

    因为物理内存被分成了三个区,所以为每个区维护一个区域描述符。区域描述符记录了每个区域内存使用情况,以及一个空闲区数组,第i个元素标记2^i个空闲页的第一个块的第一个页描述符(指针数组)。

    Linux采用了一个四级页表。

    3、Linux的内存分配机制

    Linux支持多种内存分配机制,页面分配器使用了伙伴算法。

    当一块内存过大时,会被分为两个大小相同的部分,组成一对伙伴。划分会一直持续,直到再次划分不足以容纳。释放内存时,如果一对伙伴都为空闲,则会进行合并。

    同时,LInux维护一个指针数组,数组的元素是大小为1 2 4……个单位的内存块链表的头部。

    另外,还有一种slab分配器,它也使用伙伴算法获得内存块,但是从其中切出给小的单元。例如,一个65页面的块,需要一个128页面的块来使用。

     PS:图片来自《现代操作系统》

    四、一些零碎的东西

    共享库:我们知道,一部分内存如果被多个进程共享,通常都是只读状态。如果一个进程对它进行了写操作,那么这些页面就“脏”了,系统会为另外的进程复制该页面,这就是写时复制。

    当一个程序与共享库链接时,链接器没有加载被调用的函数,而是加载了一小段能够在运行时绑定被调用函数的存根例程。关键在于,一旦一部分共享库被装载,就不需要再次装载它了。共享库会按照页面装载到内存,不需要的部分暂时不会被装载。

    另外,当共享库中的函数被修改时,并不需要重新编译调用了这个函数的程序。共享库相当于一个模块,只需要进行调用。

    另外一个问题是重定位。链接的两个主要步骤,就是符号解析和重定位。函数的重定位也就是获取实际的地址。因为共享库在内存中只有一份,而两个进程中,可能在自己进程地址空间的不同位置需要共享页面。这样,在编译共享库时,需要用一个编译选项告诉编译器,不产生使用绝对地址的指令,而是只产生使用相对地址的指令。这样,无论共享库被放置在虚拟地址空间的什么位置,指令都可以正常工作。只使用相对偏移量的代码被称作位置无关代码

    与分页有关的实现工作

    创建新进程时:操作系统要确定程序和数据初始有多大,创建一个页表,并在内存空间中为页表分配空间并进行初始化。进程被换出时,页表不必驻留内存,但是当进程在内存中时,页表也必须在内存中。为了解决内存不足的问题,可以把一部分内容放在磁盘交换区。Linux就有交换区(swap),可以通过一系列指令设置交换区大小以及查看交换区的使用。当然,硬盘I/O速度很慢,swap使用过多可能会造成速度变慢许多。好像我就有这种体验...实验室电脑比较渣,虚拟机内存设置的比较少,然后用top查看的时候就发现内存和swap区占用都很多,操作简直慢如蜗牛0 0通常是因为我开了Pycharm...

  • 相关阅读:
    数据库创建索引的缺点,和什么时候不该创建索引
    创建数据库,表,索引,删除索引,查看表中的索引和如何使用表中的索引
    java容器中 哪些是线程安全的
    java中集合类详解
    高并发 问题怎么解决
    数据库20个问题(关于事务、存储引擎、索引、悲观锁乐观锁)
    数据库事务(什么是事务)
    Application对象详解
    get和post 两种基本请求方式的区别
    BZOJ1003物流運輸 DP + SPFA
  • 原文地址:https://www.cnblogs.com/lustar/p/7840745.html
Copyright © 2020-2023  润新知