为您推荐:
精华内容
最热下载
问答
  • 4星
    679B cuihao1995 2021-03-27 18:05:19
  • 5星
    2.9MB weixin_44573410 2021-01-27 18:28:43
  • 5星
    6.88MB qq8864 2021-06-03 09:04:31
  • 5星
    20.8MB l1028386804 2021-05-18 11:29:38
  • Linux内存管理总结1地址类型物理地址:简单的理解为地址总线上的地址,直接的硬件地址;线性地址:段式转换后,但是未经过页式转换前的地址,称为线性地址;逻辑地址:程序代码中的地址未经过段式转换前的地址称为...

    Linux

    内存管理总结

    1

    地址类型

    物理地址:简单的理解为地址总线上的地址,直接的硬件地址;

    线性地址:段式转换后,但是未经过页式转换前的地址,称为线性地址;

    逻辑地址:程序代码中的地址未经过段式转换前的地址称为逻辑地址;

    下面我们通过一个图形象的理解一下:

    a4c26d1e5885305701be709a3d33442f.png

    图1 地址间的转换关系(以x86为例)

    2 什么是段式管理?

    2.1 段式管理的由来(以16位CPU为例)

    计算机的内存以‘字节’为最小单位进行线性编址,字节也是80X86对内存管理的最小元素。16位的CPU内部拥有20根地址线,它的寻址范围就是2的20次方,也就是1M的内存空间。但是16位CPU用于存放存储单元的地址的寄存器只有16位,因此只能访问65536个存储单元,64K。

    为了能够访问1M的内存空间,CPU就采用了内存分段的管理模式,并在CPU中加入了段寄存器:

    16位CPU把1M内存空间分为若干个逻辑段,每个逻辑段的要求如下:

    1

    逻辑段的起始地址必须是16的倍数,即最后4个二进制必须全为0;

    2

    逻辑段的最大容量64K,这是由16位寄存器的饿寻址空间锁决定。

    转化关系:

    逻辑地址 =

    【段标示符:段内偏移量】

    物理地址 =

    段标示符*16 + 偏移量

    问题

    :16位的CPU只有4个段寄存器,结合相应的段式管理怎么访问1M的地址空间?

    2.1 32位CPU

    32位的CPU

    内存管理仍然采用“分段”的管理模式,存储器的逻辑地址同样由段基址和偏移量两部分组成,32位于16位既有相同的地方,也有不同的地方。因为32位的PC采用了两种不同的工作模式:实模式和保护模式。

    1

    实模式

    在实模式下,32位微机的内存管理与16位微机是一致的。

    2

    保护模式

    段基地址可以长达32位,每个段的最大容量可达4G,段寄存器的值是表示段地址的选择器,用该选择器可以从内存中得到一个32位的段地址,物理地址

    = 段地址 + 段内偏移;

    a4c26d1e5885305701be709a3d33442f.png

    图2 32段式管理

    3 什么是分页管理?

    CPU的 页式内存管理单元,负责将一个线性地址,最终翻译为一个物理地址。

    从管理和效率的角度讲,线性地址被分为以固定长度为单位的组,称为页(page)。

    例如32位的机器:线性地址最大4G,可以以4k来划分,这样整个线性地址就被划分为一个tatol_page[2^20]的大数组,这个大数组我们称为页目录。

    另一类“页”,称之为物理页,或者页框,页帧。分页单元把所有的物理内存也划分为固定长度的管理单位,它的长度一般与线性地址页是相同的。

    a4c26d1e5885305701be709a3d33442f.png

    图三 分页管理

    这里注意到,这个total_page数组有2^20个成员,每个成员一个地址,这个数组就要占掉4MB的内存空间。为了节省内存,引入了二级管理模式的机器组织分页单元。

    a4c26d1e5885305701be709a3d33442f.png

    CPU的Cr3保存着进程的页目录基地址+directory+Table+offset = 具体的页。

    4 linux的内存管理

    Linux内核的设计并没有全部采用Intel所提出的段机制,仅仅是有限度的使用了分段机制。

    段的基地址全部为0x00000000

    由此得出,每个段的逻辑地址空间空间为0~4GB。因为每个段的基地址为0,因此逻辑地址到线性地址映射保持不变,也就是说,偏移量就是线性地址。Linux巧妙的绕过了段式管理机制,直接采用页式管理机制。

    Linux 2.4

    内核为了每种CPU提供统一的界面,采用了三级页式管理架构,来兼容二级,三级管理架构的CPU。Linux

    2.6内核采用了四级页管理架构。

    页全局目录(PGD) + 页中间目录(PMD) + 页表条目(PTE)

    a4c26d1e5885305701be709a3d33442f.png

    1

    内核为MMU

    设置好所有的页面表,MMU用线性地址中的PT字段作为下表找到相应的PTE,该表项中存放的就是物理页面的指针。

    2

    线性地址中的最后位段位物理页面内的相对偏移量,MMU将此偏移与目标页面的起始地址相加就得到相应的物理地址。

    上面我们从整体的角度分析了一下linux内存管理,那么具体到程序的角度我们如何来分配内存呢?

    5

    linux内核内存分配函数

    1.

    原理说明

    Linux内核中采用了一种同时适用于32位和64位系统的内存分页模型,对于32位系统来说,两级页表足够用了,而在x86_64系统中和linux2.6内核以上版本,用到了四级页表,如图2-1所示。四级页表分别为:

    l 页全局目录(Page Global

    Directory)

    l 页上级目录(Page Upper

    Directory)

    l 页中间目录(Page Middle

    Directory)

    l 页表(Page Table)

    页全局目录包含若干页上级目录的地址,页上级目录又依次包含若干页中间目录的地址,而页中间目录又包含若干页表的地址,每一个页表项指向一个页框。Linux中采用4KB大小的页框作为标准的内存分配单元。

    在实际应用中,经常需要分配一组连续的页框,而频繁地申请和释放不同大小的连续页框,必然导致在已分配页框的内存块中分散了许多小块的空闲页框。这样,即使这些页框是空闲的,其他需要分配连续页框的应用也很难得到满足。

    为了避免出现这种情况,Linux内核中引入了伙伴系统算法(buddy system)。把所有的空闲页框分组为11个块链表,每个块链表分别包含大小为1,2,4,8,16,32,64,128,256,512和1024个连续页框的页框块。最大可以申请1024个连续页框,对应4MB大小的连续内存。每个页框块的第一个页框的物理地址是该块大小的整数倍。

    假设要申请一个256个页框的块,先从256个页框的链表中查找空闲块,如果没有,就去512个页框的链表中找,找到了则将页框块分为2个256个页框的块,一个分配给应用,另外一个移到256个页框的链表中。如果512个页框的链表中仍没有空闲块,继续向1024个页框的链表查找,如果仍然没有,则返回错误。

    页框块在释放时,会主动将两个连续的页框块合并为一个较大的页框块。

    slab分配器源于 Solaris 2.4

    的分配算法,工作于物理内存页框分配器之上,管理特定大小对象的缓存,进行快速而高效的内存分配。

    slab分配器为每种使用的内核对象建立单独的缓冲区。Linux 内核已经采用了伙伴系统管理物理内存页框,因此 slab分配器直接工作于伙伴系统之上。每种缓冲区由多个

    slab 组成,每个 slab就是一组连续的物理内存页框,被划分成了固定数目的对象。根据对象大小的不同,缺省情况下一个

    slab 最多可以由 1024个页框构成。出于对齐等其它方面的要求,slab

    中分配给对象的内存可能大于用户要求的对象实际大小,这会造成一定的内存浪费。

    unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int

    order)

    __get_free_pages函数是最原始的内存分配方式,直接从伙伴系统中获取原始页框,返回值为第一个页框的起始地址。__get_free_pages在实现上只是封装了alloc_pages函数,从代码分析,alloc_pages函数会分配长度为1<

    2.6.18内核版本中,该宏定义为10。也就是说在理论上__get_free_pages函数一次最多能申请1<<10 *

    4KB也就是4MB的连续物理内存。但是在实际应用中,很可能因为不存在这么大量的连续空闲页框而导致分配失败。在测试中,order为10时分配成功,order为11则返回错误。

    struct kmem_cache *kmem_cache_create(const char *name,

    size_t size,

    size_t align, unsigned long flags,

    void (*ctor)(void*, struct kmem_cache *, unsigned long),

    void (*dtor)(void*, struct kmem_cache *, unsigned long))

    void *kmem_cache_alloc(struct kmem_cache *c, gfp_t

    flags)

    kmem_cache_create/

    kmem_cache_alloc是基于slab分配器的一种内存分配方式,适用于反复分配释放同一大小内存块的场合。首先用kmem_cache_create创建一个高速缓存区域,然后用kmem_cache_alloc从该高速缓存区域中获取新的内存块。

    kmem_cache_alloc一次能分配的最大内存由mm/slab.c文件中的MAX_OBJ_ORDER宏定义,在默认的2.6.18内核版本中,该宏定义为5,于是一次最多能申请1<<5 *

    4KB也就是128KB的连续物理内存。分析内核源码发现,kmem_cache_create函数的size参数大于128KB时会调用BUG()。测试结果验证了分析结果,用kmem_cache_create分配超过128KB的内存时使内核崩溃。

    void *kmalloc(size_t size, gfp_t flags)

    kmalloc是内核中最常用的一种内存分配方式,它通过调用kmem_cache_alloc函数来实现。kmalloc一次最多能申请的内存大小由include/linux/Kmalloc_size.h的内容来决定,在默认的2.6.18内核版本中,kmalloc一次最多能申请大小为131702B也就是128KB字节的连续物理内存。测试结果表明,如果试图用kmalloc函数分配大于128KB的内存,编译不能通过。

    void *vmalloc(unsigned long size)

    前面几种内存分配方式都是物理连续的,能保证较低的平均访问时间。但是在某些场合中,对内存区的请求不是很频繁,较高的内存访问时间也可以接受,这是就可以分配一段线性连续,物理不连续的地址,带来的好处是一次可以分配较大块的内存。图3-1表示的是vmalloc分配的内存使用的地址范围。vmalloc对一次能分配的内存大小没有明确限制。出于性能考虑,应谨慎使用vmalloc函数。在测试过程中,最大能一次分配1GB的空间。

    a4c26d1e5885305701be709a3d33442f.png

    void *dma_alloc_coherent(struct device *dev, size_t

    size,

    ma_addr_t *dma_handle, gfp_t gfp)

    DMA是一种硬件机制,允许外围设备和主存之间直接传输IO数据,而不需要CPU的参与,使用DMA机制能大幅提高与设备通信的吞吐量。DMA操作中,涉及到CPU高速缓存和对应的内存数据一致性的问题,必须保证两者的数据一致,在x86_64体系结构中,硬件已经很好的解决了这个问题,

    dma_alloc_coherent和__get_free_pages函数实现差别不大,前者实际是调用__alloc_pages函数来分配内存,因此一次分配内存的大小限制和后者一样。__get_free_pages分配的内存同样可以用于DMA操作。测试结果证明,dma_alloc_coherent函数一次能分配的最大内存也为4M。

    void * ioremap (unsigned long offset, unsigned long

    size)

    ioremap是一种更直接的内存“分配”方式,使用时直接指定物理起始地址和需要分配内存的大小,然后将该段物理地址映射到内核地址空间。ioremap用到的物理地址空间都是事先确定的,和上面的几种内存分配方式并不太一样,并不是分配一段新的物理内存。ioremap多用于设备驱动,可以让CPU直接访问外部设备的IO空间。ioremap能映射的内存由原有的物理内存空间决定,所以没有进行测试。

    如果要分配大量的连续物理内存,上述的分配函数都不能满足,就只能用比较特殊的方式,在Linux内核引导阶段来预留部分内存。

    void*

    alloc_bootmem(unsigned long size)

    可以在Linux内核引导过程中绕过伙伴系统来分配大块内存。使用方法是在Linux内核引导时,调用mem_init函数之前用alloc_bootmem函数申请指定大小的内存。如果需要在其他地方调用这块内存,可以将alloc_bootmem返回的内存首地址通过EXPORT_SYMBOL导出,然后就可以使用这块内存了。这种内存分配方式的缺点是,申请内存的代码必须在链接到内核中的代码里才能使用,因此必须重新编译内核,而且内存管理系统看不到这部分内存,需要用户自行管理。测试结果表明,重新编译内核后重启,能够访问引导时分配的内存块。

    在Linux内核引导时,传入参数“mem=size”保留顶部的内存区间。比如系统有256MB内存,参数“mem=248M”会预留顶部的8MB内存,进入系统后可以调用ioremap(0xF800000,0x800000)来申请这段内存。

    分配原理

    最大内存

    其他

    __get_free_pages

    直接对页框进行操作

    4MB

    适用于分配较大量的连续物理内存

    kmem_cache_alloc

    基于slab机制实现

    128KB

    适合需要频繁申请释放相同大小内存块时使用

    kmalloc

    基于kmem_cache_alloc实现

    128KB

    最常见的分配方式,需要小于页框大小的内存时可以使用

    vmalloc

    建立非连续物理内存到虚拟地址的映射

    物理不连续,适合需要大内存,但是对地址连续性没有要求的场合

    dma_alloc_coherent

    基于__alloc_pages实现

    4MB

    适用于DMA操作

    ioremap

    实现已知物理地址到虚拟地址的映射

    适用于物理地址已知的场合,如设备驱动

    alloc_bootmem

    在启动kernel时,预留一段内存,内核看不见

    小于物理内存大小,内存管理要求较高

    注:表中提到的最大内存数据来自CentOS5.3

    x86_64系统,其他系统和体系结构会有不同

    展开全文
    weixin_29410959 2021-05-13 09:58:57
  • 1. 内存管理策略1.1 背景1.1.1 基本硬件1.1.2 地址绑定1.1.3 逻辑地址空间与物理地址空间1.1.4 动态加载1.2 交换1.3 连续内存分配1.3.1内存保护1.3.2 内存分配1.3.3 内存碎片1.4 分段1.5 分页1.6 页表结构1.6.1 分层...

    操作系统系列内容的学习目录 → \rightarrow 操作系统学习系列内容汇总

      计算机系统的主要目的是执行程序。在执行程序及其访问数据应该至少有部分在内存里。
      为了提高CPU的利用率和响应用户的速度,通用计算机在内存里必须保留多个进程。

    1. 内存管理策略

      为了实现性能的改进, 应将多个进程保存在内存中;也就是说,必须共享内存
      内存管理算法有很多:从原始的裸机方法,到分页分段的方法,每种方法都有各自的优点和缺点。
      为特定系统选择内存管理方法取决于很多因素,特别是系统的硬件设计

    1.1 背景

      内存由一个很大的字节数组来组成,每个字节都有各自的地址(即内存地址)。
      CPU根据程序计数器的值从内存中提取指令,这些指令可能引起对特定内存地址的额外加载与存储。

    1.1.1 基本硬件

      首先需要确保每个进程都有一个单独的内存空间。单独的进程内存空间可以保护进程而不相互影响。为了分开内存空间,需要能够确定一个进程可以访问的合法地址的范围,且确保该进程只能访问这个合法地址。
      通过两个寄存器,通常为基地址界限地址。基地址寄存器含有最小的合法的物理内存地址,而界限地址寄存器指定了范围的大小
      基地址寄存器和界限地址寄存器定义逻辑地址空间如下图所示。

    在这里插入图片描述
      内存空间保护的实现是通过CPU硬件对在用户模式下产生的地址与寄存器的地址进行比较来完成的。当在用户模式下执行的程序试图访问操作系统内存或其他用户内存时,会陷入操作系统,而操作系统则将它作为致命错误来处理:

    在这里插入图片描述
      这种方案防止用户程序无意或故意修改操作系统或其他用户的代码或数据结构。只有操作系统可以通过特殊的特权指令,才能加载基地址寄存器和界限地址寄存器。
      由于特权指令只能在内核模式下执行,而只有操作系统才能在内核模式下执行,所以只有操作系统可以加载基地址寄存器和界限地址寄存器。这种方案允许操作系统修改这两个寄存器的值,而不允许用户程序修改它们。
      在内核模式下执行的操作系统可以无限制地访问操作系统及用户的内存。这项规定允许操作系统:加载用户程序到用户内存,转储出现错误的程序,访问和修改系统调用的参数,执行用户内存的I/O,以及提供许多其他服务等。
      例如,多任务系统的操作系统在进行上下文切换时,应将一个进程的寄存器的状态存到内存,再从内存中调入下个进程的上下文到寄存器。

    1.1.2 地址绑定

      通常,程序作为二进制的可执行文件,存放在磁盘上。为了执行,程序应被调入内存,并放在进程中。根据采用的内存管理,进程在执行时可以在磁盘和内存之间移动。在磁盘上等待调到内存以便执行的进程形成了输入队列(input queue) 。
      正常的单任务处理过程是:从输入队列中选取一个进程并加载到内存;进程在执行时,会访问内存的指令和数据;最后,进程终止时,它的内存空间将会释放。
      大多数系统允许用户进程放在物理内存中的任意位置。因此,虽然计算机的地址空间从00000开始,但用户进程的开始地址不必也是00000。

      链接程序或加载程序再将这些可重定位的地址绑定到绝对地址。每次绑定都是从一个地址空间到另一个地址空间的映射
      通常,指令和数据绑定到存储器地址可在沿途的任何一步中进行:
        - 编译时(compile time) :如果在编译时就已知道进程将在内存中的驻留地址,那么就可以生成绝对代码(absolute code) 。例如,如果事先就知道用户进程驻留在内存地址R处,那么生成的编译代码就可以从该位置开始并向后延伸。如果将来开始地址发生变化,那么就有必要重新编译代码。
        - 加载时(load time) :如果在编译时并不知道进程将驻留在何处, 那么编译器就应生成可重定位代码(relocatable code) 。对这种情况, 最后绑定会延迟到加载时才进行。如果开始地址发生变化,那么只需重新加载用户代码以合并更改的值。
        - 执行时(runtime time) :如果进程在执行时可以从一个内存段移到另一个内存段, 那么绑定应延迟到执行时才进行。采用这种方案需要特定硬件才行。大多数的通用计算机操作系统采用这种方法。

    1.1.3 逻辑地址空间与物理地址空间

      CPU生成的地址通常称为逻辑地址,而内存单元看到的地址(即加载到内存地址寄存器的地址)通常称为物理地址。编译时和加载时的地址绑定方法生成相同的逻辑地址和物理地址。
      然而,执行时的地址绑定方案生成不同的逻辑地址和物理地址。在这种情况下,我们通常称逻辑地址为虚拟地址(virtual address) 。程序所生成的所有逻辑地址的集合称为逻辑地址空间(logical address space) , 这些逻辑地址对应的所有物理地址的集合称为物理地址空间(physical address space) 。因此, 对于执行时地址绑定方案, 逻辑地址空间与物理地址空间是不同的。
      从虚拟地址到物理地址的运行时映射是由内存管理单元(Memory-Management Unit,MMU) 的硬件设备来完成。

    1.1.4 动态加载

      在迄今为止的讨论中,一个进程的整个程序和所有数据都应在物理内存中,以便执行。因此,进程的大小受限于内存的大小。为了获得更好的内存空间利用率,可以使用动态加载(dynamic loading) 。采用动态加载时, 一个程序只有在调用时才会加载。所有程序都以可重定位加载格式保存在磁盘上。主程序被加载到内存,并执行。当一个程序需要调用另一个程序时,调用程序首先检查另一个程序是否已加载。如果没有,可重定位链接程序会加载所需的程序到内存,并更新程序的地址表以反映这一变化。接着,控制传递给新加载的程序。
      动态加载的优点是,只有一个程序被需要时,它才会被加载。这种情况下,虽然整个程序可能很大,但是所用的部分可能很小。

    1.2 交换

      进程必须在内存中以便执行。不过,进程可以暂时从内存交换(swap) 到备份存储(backing store) , 当再次执行时再调回到内存中。交换有可能让所有进程的总的物理地址空间超过真实系统的物理地址空间,从而增加了系统的多道程序程度。

    在这里插入图片描述

    1.3 连续内存分配

      内存应容纳操作系统和各种用户进程,因此应该尽可能有效地分配内存。一种早期方法:连续内存分配
      内存通常分为两个区域:一个用于驻留操作系统,另一个用于用户进程。操作系统可以放在低内存,也可放在高内存。影响这一决定的主要因素是中断向量的位置。由于中断向量通常位于低内存,因此程序员通常将操作系统也放在低内存。
      通常,我们需要将多个进程同时放在内存中。因此我们需要考虑,如何为输入队列中需要调入内存的进程分配内存空间。在采用连续内存分配时,每个进程位于一个连续的内存区域,与包含下一个进程的内存相连。

    1.3.1内存保护

      防止进程访问不属于它的内存。

    在这里插入图片描述

    1.3.2 内存分配

      最为简单的内存分配方法之一,就是将内存分为多个固定大小的分区(partition) 。每个分区可以只包含一个进程。因此, 多道程序的程度受限于分区数
      如果使用这种多分区方法,那么当一个分区空闲时, 可以从输入队列中选择一个进程,以调入空闲分区。当该进程终止时,它的分区可以用于其他进程。这种方法现在已不再使用。
      下面所描述的方法是固定分区方案的推广(称为MVT) , 它主要用于批处理环境。
      可变分区: 操作系统有一个表,用于记录哪些内存可用和哪些内存已用。
      所有内存都可以用于用户进程,因此可以作为一大块的可用内存,称为(hole)。内存有一个集合,以包含各种大小的孔。
      从一组可用孔中选择一个空闲孔的最常用方法包括:首次适应(first-bit)、最优适应(best-bit)及最差适应(worst-fit)。
        - 首次适应: 分配首个足够大的孔。查找可以从头开始,也可以从上次首次适应结束时开始。一旦找到足够大的空闲孔,就可以停止。
        - 最优适应: 分配最小的足够大的孔。应查找整个列表,除非列表按大小排序。这种方法可以产生最小剩余孔
        - 最差适应: 分配最大的孔。同样,应该查找整个列表,除非列表按大小排序。这种方法可以产生最大剩余孔
      模拟结果显示,首次适应和最优适应在执行时间和利用空间方面都好于最差适应。首次适应和最优适应在利用空间方面难分伯仲,但是首次适应要更快些。

    1.3.3 内存碎片

      用于内存分配的首次适应和最优适应算法都有外部碎片(external fragmentation) 的问题。
      随着进程加载到内存和从内存退出,空闲内存空间被分为小的片段。当总的可用内存之和可以满足请求但并不连续时,这就出现了外部碎片问题:存储被分成了大量的小孔
      这个问题可能很严重。在最坏情况下,每两个进程之间就有空闲(或浪费的)块。如果这些内存是一整块,那么可能可以再运行多个进程。
      选择首次适应或者最优适应,可能会影响碎片的数量。(对一些系统来说,首次适应更好;对另一些系统,最优适应更好)。
      另一因素是从空闲块的哪端开始分配。(哪个是剩余的块,是上面的还是下面的?)不管使用哪种算法,外部碎片始终是个问题。

      内存碎片可以是内部的,也可以是外部的。假设有一个18464字节大小的孔,并采用多分区分配方案。假设有一个进程需要18462字节。如果只能分配所要求的块,那么还剩下一个2字节的孔。维护这一小孔的开销要比孔本身大很多。
      因此,通常按固定大小的块为单位(而不是字节)来分配内存。采用这种方案,进程所分配的内存可能比所需的要大。这两个数字之差称为内部碎片(internal fragmentation) , 这部分内存在分区内部, 但又不能用。

      外部碎片问题的一种解决方法紧缩(compaction) 。它的目的是移动内存内容, 以便将所有空闲空间合并成一整块。然而,紧缩并非总是可能的。如果重定位是静态的,并且在汇编时或加载时进行的,那么就不能紧缩。只有重定位是动态的,并且在运行时进行的,才可采用紧缩。如果地址被动态重定位,可以首先移动程序和数据,然后再根据新基地址的值来改变基地址寄存器。如果能采用紧缩,那么还要评估开销。最简单的合并算法是简单地将所有进程移到内存的一端,而将所有的孔移到内存的另一端,从而生成一个大的空闲块。这种方案比较昂贵。
      外部碎片化问题的另一个可能的解决方案是:允许进程的逻辑地址空间是不连续的;这样,只要有物理内存可用,就允许为进程分配内存。
      有两种互补的技术可以实现这个解决方案:分段分页,这两个技术也可以组合起来。

    1.4 分段

      分段(segmentation) 就是支持这种用户视图的内存管理方案。逻辑地址空间是由一组段构成。每个段都有名称和长度。地址指定了段名称和段内偏移。因此用户通过两个量来指定地址:段名称段偏移
      为了实现简单起见,段是编号的,是通过段号而不是段名称来引用。因此,逻辑地址由有序对(two tuple) 组成:
    <段号,偏移>

    1.5 分页

      分段允许进程的物理地址空间是非连续的。分页是提供这种优势的另一种内存管理方案。
      然而,分页避免了外部碎片和紧缩,而分段不可以。分页也避免了将不同大小的内存块匹配到交换空间的麻烦问题。在分页引入之前采用的内存管理方案都有这个问题。
      这个问题出现的原因是:当位于内存的代码和数据段需要换出时,应在备份存储上找到空间。备份存储也有同样的与内存相关的碎片问题,但是访问更慢,因此紧缩是不可能的。
      由于比早期方法更加优越,各种形式的分页为大多数操作系统采用,包括大型机的和智能手机的操作系统。实现分页需要操作系统和计算机硬件的协作。

      采用分页方案不会产生外部碎片:每个空闲帧都可以分配给需要它的进程。不过,分页有内部碎片。
      分页的优点之一是可以共享公共代码。对于分时环境,这种考虑特别重要。

    1.6 页表结构

      组织页表的一些最常用技术,包括分层分页哈希页表倒置页表

    1.6.1 分层分页

      大多数现代计算机系统支持大逻辑地址空间( 2 32 2^{32} 232~ 2 64 2^{64} 264)。在这种情况下,页表本身可以非常大。
      例如,假设具有32位逻辑地址空间的一个计算机系统。如果系统的页大小为4KB( 2 12 2^{12} 212),那么页表可以多达100万的条目( 2 32 2^{32} 232/ 2 12 2^{12} 212)。假设每个条目有4字节,那么每个进程需要4MB物理地址空间来存储页表本身。显然,我们并不想在内存中连续地分配这个页表。这个问题的一个简单解决方法是将页表划分为更小的块,完成这种划分有多个方法。一种方法是使用两层分页算法,就是将页表再分页:

    在这里插入图片描述

    1.6.2 哈希页表

      处理大于32位地址空间的常用方法是使用哈希页表(hashed page table) , 采用虚拟页码作为哈希值。哈希页表的每一个条目都包括一个链表,该链表的元素哈希到同一位置(该链表用来解决处理碰撞)。每个元素由三个字段组成:1)虚拟页码,2)映射的帧码,3)指向链表内下一个元素的指针。
      该算法工作如下:虚拟地址的虚拟页码哈希到哈希表,用虚拟页码与链表内的第一个元素的第一个字段相比较。如果匹配,那么相应的帧码(第二个字段)就用来形成物理地址;如果不匹配,那么与链表内的后续节点的第一个字段进行比较,以查找匹配的页码。

    在这里插入图片描述

    1.6.3 倒置页表

      通常,每个进程都有一个关联的页表。该进程所使用的每个页都在页表中有一项(或者每个虚拟页都有一项,不管后者是否有效)。这种表示方式比较自然,因为进程是通过虚拟地址来引用页的。操作系统应将这种引用转换成物理内存的地址。
      由于页表是按虚拟地址排序的,操作系统可计算出所对应条目在页表中的位置,可以直接使用该值。这种方法的缺点之一是,每个页表可能包含数以百万计的条目。这些表可能需要大量的物理内存,以跟踪其他物理内存是如何使用的。
      为了解决这个问题, 我们可以使用倒置页表(inverted page table) 。对于每个真正的内存页或帧,倒置页表才有一个条目。每个条目包含保存在真正内存位置上的页的虚拟地址以及拥有该页进程的信息。
      因此,整个系统只有一个页表,并且每个物理内存的页只有一条相应的条目。下图显示了倒置页表的工作原理;由于一个倒置页表通常包含多个不同的映射物理内存的地址空间,通常要求它的每个条目保存一个地址空间标识符。地址空间标识符的保存确保了,具体进程的每个逻辑页可映射到相应的物理帧。

    在这里插入图片描述
      系统内的每个虚拟地址为一个三元组:〈进程id,页码,偏移〉。
      每个倒置页表条目为二元组〈进程id,页码〉,这里进程id用来作为地址空间的标识符。当发生内存引用时,由〈进程id,页码〉组成的虚拟地址被提交到内存子系统。然后,搜索倒置页表来寻找匹配。如果找到匹配条目,如条目i,则生成物理地址〈i,偏移〉。如果找不到匹配,则为非法地址访问。
      缺点: 1. 虽然减少了存储每个页表所需的内存空间,但增加了由于引用页而查找页表所需的时间。
          2. 采用倒置页表的系统在实现共享内存的时候会有困难。

    展开全文
    HUAI_BI_TONG 2021-09-01 16:29:52
  • 这里写目录标题内存的基础知识什么是内存什么作用进程运行的基本原理 内存的基础知识 什么是内存什么作用 内存是用于存放数据的硬件。程序执行前需要先放到内存中才能被CPU处理,如果高速CPU直接和慢速的外...

    内存的基础知识

    1. 什么是内存、有什么作用

    • ⭐内存是用于存放数据的硬件。程序执行前需要先放到内存中才能被CPU处理,如果高速CPU直接和慢速的外存进行数据的交互,有速度矛盾

    思考:
    在多道程序环境下,系统中会有多个程序并发执行,也就是说会有多个程序数据需要同时放入内存中,那么,如何区分各个程序的数据是放在什么地方呢?
    方案:
    给内存的存储单元编地址

    1. 内存地址从0开始,每个地址对应一个存储单元
    2. 内存中也有一个一个的“小房间”,每个小房间就是以一个存储单元
    3. 如果计算机“按字节编址”,则每个存储单元大小为1字节,即1B,即8个二进制位
    4. 如果字长16位的计算机“按字编址”,则每个存储单元大小为1个字,每个字的大小为16个二进制位

    补充知识:几个常用的数量单位
    一台电脑内存有4GB:是指该内存中可以存放4*230个字节,如果是按字节编址的,就是有4 * 230= 232个存储单元,这么多存储单元需要232个地址才能一个一个表示,所以地址需要用32个二进制位来表示(0 ~ 232-1)
    210 = 1K
    220 = 1M
    230 = 1G

    2. 进程运行的基本原理

    1. 指令:

      1. 以执行为例x=x+1:首先系统会将代码编译成CPU可以识别的机器语言(指令):
        在这里插入图片描述
        CPU会依次执行程序段中的指令
        1. 指令的前面几位为操作码(红色),可以告诉CPU让它做什么(数据传达、加法…)
        2. 后面的为参数,如指令1,表示从01001111中取出数据,将数据传输到00000011的寄存器中
      2. 具体的执行过程:
        代码会“翻译”成CPU可以识别的指令,这些指令会告诉CPU该去内存的哪个地址存/取数据,这个数据应该做什么样的处理
        在这里插入图片描述
        首先CPU根据 指令1: 从01001111中取出数据,并将数据传输到00000011的寄存器中,接着执行 指令2: 将寄存器00000011中的数据 + 00000001(即+1),再执行 指令3: 将寄存器00000011中的计算结果,再传回到地址0101111中
    2. ⭐逻辑地址&相对地址:

      思考:
      上面的例子中直接给出了数据的实际存放地址(物理地址),但在实际中生产机器指令的时候并不知道该进程的数据会被放到什么位置,所以编译生成的指令一般使用的是逻辑地址(相对地址)

      • 指令中的地址:编译时产生的指令,只关心“相对地址”,实际放入内存中时再想办法通过起始地址找到“绝对地址”
      • 相对=逻辑,绝对=物理
    3. ⭐从写程序到程序运行:

      1. 编译:由编译程序将用户源代码文件(* .c文件)编译成若干个目标模块(* .o文件)(编译就是把高级语言翻译成机器语言)
      2. 链接:由连接程序将编译后形成的一组目标模块,以及所需库函数链接在一起,形成一个完整的装入模块
      3. 装入:由装入程序将装入模块装入内存运行
        在这里插入图片描述
    4. ⭐装入的三种方式:

      1. 绝对装入:——装入模块直接用绝对地址
        1. 在编译时,如果程序员知道程序将放到内存的哪个位置,编译程序将产生绝对地址的目标代码。装入程序按照装入模块中的地址,将程序和数据装入内存(编译、链接后得到的装入模块的指令直接就使用了绝对地址
        2. 缺点:绝对装入只适用于单道程序环境
          在这里插入图片描述
      2. 静态重定位:——装入模块用逻辑地址,装入时转换为绝对地址
        1. 编译、链接后的装入模块的地址都是从0开始的逻辑地址,指令中使用的地址、数据存放的地址都是相对于起始地址而言的逻辑地址。可根据内存的当前情况,将装入模块装入到内存的适当位置,装入时对地址进行“重定位”,将逻辑地址转换为物理地址(地址变换是在装入时一次性完成的
        2. 缺点:静态重定位时在一个作业装入内存时,必须分配其要求的全部内存空间,如果没有足够的内存,就不能装入该作业。作业一旦进入内存后,在运行期间就不能再移动,也不能再申请内存空间
          在这里插入图片描述
      3. ⭐⭐动态重定位:————装入模块用逻辑地址,装入内存也使用逻辑地址,要用时才转换为绝对地址
        1. 编译、链接后的装入模块的地址都是从0开始的逻辑地址,装入程序把装入模块装入内存后,并不会立即把逻辑地址转换为物理地址,而是 把地址转换推迟到程序真正要执行时 才进行。因此 装入内存后所有的地址依然是逻辑地址 。这种方式需要一个重定位寄存器的支持
        2. 优点
          1. 允许程序在内存中发生移动
          2. 可以将程序分配到不连续的存储区中:再程序运行前只需要装入它的部分代码即可投入运行,然后再程序运行期间,根据需要动态申请分配内存
          3. 便于程序段的共享,可以向用户提供一个比存储空间大得多的地址空间

      在这里插入图片描述

    5. 链接的三种方式:

      1. 静态链接:在程序运行之前,将各个目标模块以及它们所需的库函数链接成一个完整的可执行文件(装入模块),之后便不再拆开
        在这里插入图片描述
      2. 装入时动态链接:将各个目标模块装入内存时,边装入边链接的链接方式
        在这里插入图片描述
      3. 运行时动态链接:在程序执行中需要该目标模块时,才对他进行链接。其优点是便于修改和更新,便于实现对目标模块的分享
        在这里插入图片描述

    内存管理

    1. 1. 操作系统负责内存空间的分配与回收(下一节内容)

      1. 操作系统要如何记录哪些内存区域已经被分配出去了,哪些还空闲?
      2. 当一个进程要调入内存时,很多位置都可以放,那么要放到哪个位置?
      3. 当进程运行结束后,如何将进程占用的内存空间回收?
      1. 传统的存储管理方式:(一次性、驻留性
        1. 连续分配方式:
          1. 单一连续分配
          2. 固定分区分配
          3. 动态分区分配
        2. 非连续分配方式:
          1. 基本分页存储管理
          2. 基本分段存储管理
          3. 基本段页式存储管理
      2. 虚拟内存技术(见下)
    2. 2. 操作系统需要提供某种技术从逻辑上对内存空间扩充

      4GB内存运行60GB的游戏
      早期的计算机内存很小,因此会出现内存不够的情况。后来人们引入了覆盖技术,用来解决“程序超过物理内存总和”的问题

      1. 覆盖技术 :

        在同一个程序或进程中的
        1. 覆盖技术的思想:将程序分成多个段(多个模块)。常用的段常驻内存,不常用的段需要调入内存
        2. 内存中分为一个“固定区”和若干个“覆盖区”
        3. 需要常驻内存的段放在 “固定区” 中,调入后就不再调出(除非运行结束)
        4. “覆盖区” 中的程序段会在运行当中根据需要调入调出
        5. 必须由程序员声明覆盖结构,操作系统完成自动覆盖
        6. 缺点:对用户不透明,增加了编程困难,用于早期操作系统,已成为历史
      2. 交换技术:

        在不同的进程或作业之间
        1. 交换技术的思想:内存空间紧张时,系统将内存中的某些进程暂时换出外存,把外存中某些已经具备运行条件的进程换入内存(进程在内存与磁盘之间动态调度)
        2. 注意:PCB会常驻内存,不会被换出,保存进程的信息,保证管理换出的进程
        3. 思考:
          1. 应该在外存的什么位置保存被换出的进程?
            具有对换功能的操作系统中,通常把磁盘空间分为文件区和对换区两部分。文件区主要用于存放文件,主要追求存储空间的利用率,因此对文件区空间的管理采取离散分配的方式;对换区空间只占磁盘空间的一小部分,被换出的数据就存储在兑换区,兑换区通常采用I/O速度更快的连续分配方式
          2. 什么时候应该交换?
            交换通常在许多进程运行且内存吃紧时进行,而系统负荷降低就暂停。例如:在发现许多进程运行时经常发生缺页,就说明内存紧张,此时可以换出一些进程;如果缺页率明显下降,就可以暂停换出
          3. 应该换出哪些程序?
            可以优先换出阻塞态的进程,可以换出优先级比较低的进程,同时防止优先级低的进程发生饥饿,会考虑进程在内存的驻留时间
      3. ⭐虚拟存储技术

        1. 传统存储管理方式的特征和缺点:

          1. 一次性:作业必须一次性全部装入内存后才能开始运行。会造成两个问题:
            1. 作业很大时,不能全部装入内存,导致大作业无法运行
            2. 当大量作业要求运行时,由于内存无法容纳所有作业,因此只有少量作业能运行,导致多道程序并发度下降
          2. 驻留性:一旦作业被装入内存,就会一直驻留在内存中,直至作业运行结束。事实上,在一个时间段内,只需要访问作业的一小部分数据即可正常运行
        2. 局部性原理

          1. 时间局部性:如果执行了程序中的某条指令,那么不久后这条指令很有可能再次执行;如果某条数据被访问过,那么不久之后该数据很有可能再次被访问(因为程序中存在大量的循环)
          2. 空间局部性:一旦程序访问了某个存储单元,在不久之后其附近的存储单元也很有可能被访问(因为很多数据在内存中都是连续存放的)
          3. 高速缓冲技术:
            1. 思想:将近期会频繁访问到的数据放到更高速的存储器中,暂时用不着的数据放在更低速的存储器中
            2. 存取速度:寄存器>高速缓存>内存>外存
            3. 容量:外存>内存>高速缓存>寄存器
            4. 应用快表机构就是将近期常访问的页表项副本放在更高速的联想寄存器当中
        3. 虚拟内存的定义和特征

          1. 定义:
            1. 基于局部性原理,在程序装入时,可以将程序中很快会用到的部分装入内存,暂时用不到的部分留在外存,就可以让程序开始执行
            2. 在程序执行的过程中,当所访问的信息不再内存中时,由操作系统负责将所需信息从外存调入内存,然后继续执行程序
            3. 内存空间不够,由操作系统负责将内存中暂时不用的信息换出到外存
            4. 在操作系统的管理下,在用户看来似乎有一个比实际内存大得多的内存,这就是虚拟内存(逻辑上的扩充
          2. 易混知识点
            1. 虚拟内存的最大容量是由计算机的地址结构(CPU寻址范围)确定的
            2. 虚拟内存的实际容量 = min(内存和外存容量之和,CPU寻址范围)

            例:某计算机地址结构为32位,按字节编址,内存大小为512MB,外存大小为2GB.
            1. 虚拟内存的最大容量为232 B = 4GB
            2. 虚拟内存的实际容量 = min(512MB+2GB,232 )= 512MB+2GB

          3. 虚拟内存的特征:
            1. 多次性:无需在作业运行时一次性全部装入内存,而是允许被分成多次调入内存
            2. 对换性:在作业运行时无需一直常驻内存,而是允许作业在运行过程中,将作业换入、换出
            3. 虚拟性:从逻辑上扩充了内存的容量,使用户看到的内存容量,远大于实际的容量
        4. 如何实现虚拟内存技术

          虚拟内存技术,允许一个作业分多次调入内存。如果采用连续分配方式,会不方便实现。因此,虚拟内存的实现需要建立在离散分配的内存管理方式基础上

          1. 传统的非连续分配存储管理:基本分页存储管理、基本分段存储管理、基本段页式存储管理
          2. 改进后的虚拟内存的实现:请求分页存储管理、请求分段存储管理、请求段页式存储管理
          3. 主要区别:
            1. 请求调页)在程序执行过程中,当所访问的信息不在内存中,由操作系统负责将信息从外存调入内存,然后继续执行程序
            2. 页面置换)若内存空间不够,由操作系统负责将内存中暂时用不到的信息换出到外存
        5. 请求分页管理方式

          1. 页表机制
            1. 与基本分页中页表的区别:
              1. 与基本分页管理相比,请求分页管理中,为了实现“请求调页”,操作系统需要知道每个页面是否已经调入内存;如果还没调入,那么也需要知道该页面在外存中存放的位置
              2. 当内存空间不够时,要实现“页面置换”,操作系统需要通过某些指标来决定到底换出哪个页面;有的页面没有被修改过,就不用再浪费时间写出外存了。有的页面修改过,就需要将外存中的旧数据覆盖,因此,操作系统也需要记录各个页面是否被修改的信息
            2. 请求分页中的页表: 在这里插入图片描述
          2. 缺页中断机制
            1. 过程:
              1. 假设此时要访问逻辑地址=(页号,页内偏移量)=(0,1024)
              2. 在请求分页系统中,每当要访问的页面不在内存时,便产生一个缺页中断,然后由操作系统的缺页中断处理程序处理中断
              3. 此时缺页的进程阻塞,放入阻塞列表,调页完成后再将其唤醒,放回就绪队列
              4. 如果此时内存有空闲块,则为进程分配一个空闲块,将所缺页面装入该块,并修该页表中相应的页表项
              5. 如果内存中没有空闲块,则由页面置换算法选择一个页面淘汰,若该页面在内存期间被修改过,则要将其写回内存未修改过的页面不用写回内存
            2. 缺页中断是因为当前执行的指令想要访问的目标页面为调入内存而产生的,因此属于内中断(信号来源于CPU内部,又因为是由错误条件引起的,可能被故障处理程序修复,所以归为内中断中的故障
            3. 一条指令在执行期间,可能产生多次缺页中断。(如:copy A toB,而A、B可能属于不同的页面,则有可能发生两次中断)
          3. 地址变换机构
            1. 基本分页与请求分页的主要区别
              1. 在程序执行过程中,当所访问的信息不在内存时,由操作系统负责将所有信息从外存调入内存,然后继续执行程序。
              2. 若空间不够,由操作系统负责将内存中暂时用不到的信息换出到外存
            2. 新增步骤
              1. 请求调页(查到页表项是进行判断)
              2. 页面置换(需要调入页面,但没有空闲内存块时进行)
              3. 需要修改请求页表中新增的表项
            3. 过程:
              在这里插入图片描述
            4. 补充细节
              1. 只有==“写指令”才需要修改“修改位”==。并且,一般来说只需要修改快表中的数据,只有将快表项删除时才需要写回内存中的慢表。这样可以减少访存次数
              2. 和普通中断处理一样,缺页中断也需要保留CPU现场
              3. 需要用某种“页面置换算法”来决定一个换出页面
              4. 换入/换出页面都需要启动慢速的I/O操作,可见,如果换入/换出太频繁,会有很大开销
              5. 快表中的页面一定是在内存中的:如果有页面被换出外存,则快表中相应的页表项也要删除,否则可能访问错误的页面
              6. ⭐页面调入内存后,需要修改慢表,同时也要将表项直接复制到快表当中,这样若发生缺页,则地址变换步骤是:查快表(未命中)→查慢表(发现未调入内存)→调页(调入的页面对应的表项会直接加入快表)→查快表(命中) →访问目标内存单元
        6. ⭐页面置换算法

          页面的换入/换出需要磁盘I/O,会有较大的开销,因此好的页面置换算法应该追求更好的缺页率

          1. 最佳置换算法(OPT)

            1. 思想:每次选择淘汰的页面将是以后永远不使用,或者在最长时间内不再被访问的页面,这样就可以保证最低的缺页率
            2. 例: 在这里插入图片描述
            3. 算法性能:
              1. 整个过程中缺页中断发生9次,页面置换发生6次。(并不是发生缺页就会有页面置换,只有在内存已满时才会换入换出)
              2. 缺页率 = 9 / 20 = 45%
              3. 最佳置换算法需要知道之后需要访问的是哪个页面,但实际上,操作系统是无法预料页面访问序列的,故,最佳置换算法是无法实现的
          2. 先进先出置换算法(FIFO)

            1. 思想:每次选择置换的页面是最早调入内存的页面
            2. 实现方法:把调入内存的页面根据调入的先后顺序排成一个队列,需要换出页面时选择队头页面即可。队列最大长度取决于系统为进程分配了多少内存块
            3. 例1:分配三个内存块时,缺页次数:9次 在这里插入图片描述
            4. 例2:分配四个内存块时,缺页次数:10次 在这里插入图片描述
            5. belady异常:
              1. 当为进程分配的物理块数增大时,缺页次数不减反增的现象
              2. 只有FIFO算法会产生belady异常
              3. 虽然FIFO算法实现简单,但该算法与进程实际运行时的规律不适应,因为先进入的页面也有可能被最经常访问,若总是换出先入的页面(简单粗暴),导致算法性能不高
          3. 最近最久未使用置换算法(LRU)

            1. 思想:每次淘汰的页面是最近最久未使用的页面
            2. 实现方法:赋予每个页面对应的页表项中用访问字段记录该页面至上次被访问以来所经历的时间 t 当需要淘汰一个页面时,选择 t 值最大的页面换出
            3. 例: 在这里插入图片描述
            4. 动手做题时,若需要淘汰页面,逆向数最后出现的,就是要淘汰的
            5. 算法性能
              1. 算法性能好,最接近最佳置换算法
              2. 但是实现困难,需要专门的硬件支持,开销比较大
          4. 时钟置换算法(CLOCK)

            1. 思想:一种性能和开销较为均衡的算法,又称CLOCK算法,或最久未用算法NRU
            2. 实现方法 :为每一个页面设置一个访问位,再将内存中的页面全都用链接指针连接成一个循环队列。
              1. 当某页被访问时,将其访问位置为1
              2. 当需要淘汰一个页面时,开始扫描,只需要检查页的访问位。
                1. 如果是0,就选择该页换出;如果是1,则将它置为0,暂时不换出,继续检查下一个页面,
                2. 若第一轮扫描中所有页面都是1,则将这些页面的访问位依次置为0后(第一轮中边扫边置0),进行第二轮扫描(第二轮中一定会有页面的访问位是0,故CLOCK算法中选择一个淘汰页面最多会经过两轮扫描)
            3. 例子:假设系统为某进程分配了五个内存块,并考虑到有以下页面号引用串:1、3、4、2、5、6、3、4、7
              1. 访问6号页之前:1号页(1),3号页(1),4号页(1),2号页(1),5号页(1)
              2. 访问到6号页:6号页不再内存中,需要换出一页,开始扫描此时扫描指针指向1号页
                1. 第一轮扫描:1号页(0),3号页(0),4号页(0),2号页(0),5号页(0)此时扫描指针指回了1号页
                2. 第二轮扫描:6号页(1),3号页(0),4号页(0),2号页(0),5号页(0)此时扫描指针指向下一个页面,即3号页
              3. 访问到3号页:6号页(1),3号页(1),4号页(0),2号页(0),5号页(0)
              4. 访问到4号页:6号页(1),3号页(1),4号页(1),2号页(0),5号页(0)
              5. 访问到7号页:7号页不再内存中,需要换出一页,开始扫描
                1. 第一轮扫描(从3号页开始扫描):6号页(1),3号页(0),4号页(0),7号页(1),5号页(0) 此时扫描指针指向下一个页面,即5号页
          5. 改进型的时钟置换算法

            1. 思想:之前学过,若被换出的页面没有被修改过,则不需要执行I/O操作写回外存,只有被修改过的页面才需要写回外存
            2. 因此,除了考虑一个页面是否被访问过之外,还需要考虑被访问的页面是否被修改
            3. 实现方法:再设置一个修改位,=1时表示修改过、=0时表示未修改;则利用二元组:==(访问位,修改位)==标识一个页面
            4. 算法规则:将所有可能被置换的页面排成一个循环队列
              1. 第一轮:将当前位置开始扫描到第一个(0,0)的帧用于替换。本轮扫描不修改任何标志位
              2. 第二轮:若第一轮扫描寻找失败,则重新扫描,查找第一个(0,1)的帧用于替换。本轮将所有扫描过的页面的访问位设置为0
              3. 第三轮:若第二轮扫描寻找失败,则重新扫描,查找第一个(0,0)的帧用于替换。本轮扫描不修改任何标志位
              4. 第四轮:若第三轮扫描寻找失败,则重新扫描,查找第一个(0,1)的帧用于替换。
            5. 由于第二轮中会将所有访问位设置为0,故在第三轮或第四轮中一定回找到目标页面。故扫描次数一定不会超过四轮
            6. 扫描置换优先级:
              1. 第一轮:最近未访问,且没修改的页面
              2. 第二轮:最近未访问,且修改过的页面
              3. 第三轮:最近访问过,且没修改的页面
              4. 第四轮:最近访问过,且修改过的页面
          6. ⭐页面置换算法的比较: 在这里插入图片描述

          7. 页面分配策略

            1. 驻留集
              1. 指请求分页存储管理中给进程分配的物理快的集合
              2. 在采用了虚拟存储技术的系统中,驻留集的大小一般小于进程的总大小
                ① 若驻留集太小,会导致缺页频繁,系统要花很大的代价处理缺页,时机用于推进进程运行的时间就会变少
                ② 若驻留集太大,又会导致多道程序并发度下降,资源利用率降低
                ③ 选择合适大小的驻留集
            2. 页面分配、置换策略
              1. 驻留集大小是否会变 ?
                固定分配:操作系统为每一个进程分配一组固定数目的物理块,在进程运行期间不再改变。即,驻留集大小不变
                可变分配:先为每个进程分配一定数目的物理块,在进程运行期间,可以根据情况适当的增加或减少,即,驻留集大小可变
                2.置换出去的页面是否只能是自己的 ?
                局部置换:发生缺页时只能选择进程自己的物理块进行置换
                全局置换:可以将操作系统保留的空闲物理块分配给缺页进程,也可以将别的进程持有的物理块置换到外存,再分配给缺页进程
              1. 固定分配局部置换题目中常用条件,表示为进程分配的物理块数量不变
                1. 系统为每个进程分配一定数量的物理块,在整个运行期间不改变。若进程在运行中发生缺页,也只能从该进程在内存中的页面选出一个来换出,然后再调入需要的页面。
                2. 性能:灵活性差,很难在刚开始就确定分配多少物理块比较合适
              2. 可变分配全局置换
                1. 系统为每个进程分配一定数量的物理块。操作系统就会保持一个空闲物理块队列。当某进程发生缺页时,从空闲物理块队列中取出一块分配给该进程;若已无空闲物理块,则选择一个未锁定(系统会锁定一些很重要的页面)的页面换出到外存,再将物理块分配给缺页的进程
                2. 性能:
                  1. 只要某进程发生缺页,都能获得新的物理块
                  2. 仅当空闲物理块使用完时,系统才会为缺页进程选择一个页面,并且这个页面可以是系统中的任何一个进程的页
                  3. 故被调出页面的那个进程拥有的物理块就会减少,缺页率也会增加(躺枪)
              3. 可变分配局部置换
                1. 系统为每个进程分配一定数量的物理块。当某进程发生缺页时,只允许从该进程自己的页面中选择一个置换出去
                2. 性能:(容易让系统根据各进程的缺页率,动态地合理分配物理块
                  1. 这样如果一个进程频繁的缺页,系统就会为其分配更多的物理块
                  2. 反之,如果进程再运行中缺页率很低,则可以适当减少分配给该进程的物理块
              4. 不存在固定分配全局置换——只要是全局置换,则该进程的物理块数量一定会变
            3. 调入页面时机
              1. 预调页策略运行前调入):由于局部性原理,一次调入若干个相邻的页面比调入一个页面更高效,但是这样调入的页面并不能保证之后一定会被访问到,所以又是低效的,故这种策略往往用于进程的首次调入
              2. 请求调页策略运行时调入):进程再运行时发现缺页时才将所缺页面调入,这样调入的页面在之后一定会被访问到,但是频繁的I/O操作又会增大系统的开销
            4. 从何处调页
              1. 系统拥有足够对换区空间
                ① 页面调入、调出都是再内存和对换区之间进行,这样保证页面调入调出速度很快
                ② 在进程运行前需要将进程相关的数据从文件区复制到对换区 在这里插入图片描述
              2. 系统缺少足够的对换区空间
                ① 凡是不会被修改的数据都直接从文件区调入,由于这些页面不需要在被调出,写回磁盘,如果下次用到再从文件区调入即可
                ② 对于可能被修改的部分,在内存和对换区换入换出 在这里插入图片描述
              3. Unix方式
                ① 运行前将所有数据放在文件区,故未使用过的文件都从文件区调入内存
                ② 一旦数据被使用过,则在对换区与内存换入换出 在这里插入图片描述
            5. 抖动(颠簸)现象
              1. 刚换入内存的页面马上又要换出到外存,或刚换出到外存的页面马上要换入内存,这样频繁的页面调度行为称为抖动或颠簸
              2. 产生的原因:进程频繁访问的页面数目高于可用的物理块数(分配给进程的物理块不够用)
            6. 工作集

              为进程分配的物理块太少,会使进程发生抖动现象
              为进程分配到额物理块太多,又会使得系统整体的并发度降低,降低某些资源的利用率

              1. 为了研究该为每个进程分配多少个物理块,提出了进程“工作集”的概念
              2. 定义:
                1. 驻留集:请求分页存储管理中给进程分配的内存块的集合
                2. 工作集:在某段时间间隔内,进程实际访问的页面合集

                例如:某进程的页面访问序列如下,窗口尺寸为4,各时刻的工作集为? 在这里插入图片描述

              3. 工作集的大小可能小于窗口大小,实际过程中操作系统可以统计进程工作集的大小,根据工作集大小给进程分配若干内存块——如:窗口尺寸大小为5,但一段时间后监测到进程的 工作集大小最大为3,那么说明改进程拥有很好的局部性,可以给进程分配3个或3个以上的内存块即可满足进程的运行需要
              4. 一般来说,驻留集大小不能小于工作集大小,否则进程运行过程中将频繁调页
    3. 3. 操作系统需要提供地址转换功能

      负责程序的逻辑地址与物理地址的转换

      三种装入方式

    4. 4. 操作系统需要提供内存保护功能

      保证各个进程在各自的存储空间内运行,互不干扰 在这里插入图片描述
      内存保护可采取的两种方法:

      1. 方法一:在CPU中为每个进程设置一对上下限寄存器,存放进程的上下限地址。当进程的一条指令需要访问数据时,会检查该指令访问的数据地址是否在该指令所属的进程的地址上下限中,即是否越界
        在这里插入图片描述
      2. 方法二:采用重定位寄存器(又称基址寄存器)和界地址寄存器(又称限长寄存器)进行越界检查。重定位寄存器中存放的时进程的 起始物理地址,界地址寄存器存放的是进程的 最大逻辑地址
        在这里插入图片描述
    展开全文
    Qmilumilu 2021-02-07 13:00:41
  • 11G中的自动内存管理是Oracle数据库中又一新的里程碑,通过新参数MEMORY_TARGET来代替PGA和SGA的配置,ORACLE在运行过程中会自动调节PGA和SGA的大小,跟10G中SGA自动管理的方法基本一直。MEMORY_TARGET参数可支持动态...

    Oracle一直不停的在为Orace数据库的自动化管理努力着,11G中的自动内存管理是Oracle数据库中又一新的里程碑,通过新参数MEMORY_TARGET

    来代替PGA和SGA的配置,ORACLE在运行过程中会自动调节PGA和SGA的大小,跟10G中SGA自动管理的方法基本一直。MEMORY_TARGET参数

    可支持动态修改,同样的MEMORY_TARGET也有一个限制最大内存分配的参数:MEMORY_MAX_TARGET,这个跟SGA_TARGET和

    SGA_MAX_TARGET的关系一样。

    启用内存自动管理

    DBCA创建数据库的过程中可直接设置MEMORY_TARGET参数

    数据库创建完成后,可通过以下方式手动配置MEMORY_TARGET

    ALTER SYSTEM SET MEMORY_TARGET = 256M;

    ALTER SYSTEM SET SGA_TARGET = 0;

    ALTER SYSTEM SET PGA_AGGREGATE_TARGET = 0;

    后两步是禁用SGA和PGA的自动管理. 让ORACLE对内存实行自动调节. 还可以设置MEMORY_MAX_TARGET参数限制Oracle可自动分配的最大

    内存.该参数不能动态调整,调整后需要重新启动数据库.正确设置MEMORY_MAX_TARGET参数的方法:

    ALTER SYSTEM SET MEMROY_MAX_TARGET=256M SCOPE=SPFILE;

    查看MEMORY_TARGET内存的设置情况:

    SQL> show parameter target

    NAME TYPE VALUE

    ------------------------------------ ----------- ------------------------------

    archive_lag_target integer 0

    db_flashback_retention_target integer 1440

    fast_start_io_target integer 0

    fast_start_mttr_target integer 0

    memory_max_target big integer 256M

    memory_target big integer 256M

    pga_aggregate_target big integer 0

    sga_target big integer 0

    iOS----ARC(自动内存管理)

    1.ARC是什么呢,有什么用? ARC是苹果官方推出的帮助我们苹果开发工程师管理内存的一种自动内存管理机制,它的前身是MRC,也就是手动内存管理: 2.ARC的基本原理是什么? ARC是编译器(时)特 ...

    cocos2d-x学习之自动内存管理

    一.自动内存管理 1)概述 C++语言默认是没有提供自动内存管理的.使用者需要自己分配,自己释放.在cocos2d-x里提供了一个自动内存管理的方案.主要是通过CCObject来提供的,用户只要继承了 ...

    JVM自动内存管理学习笔记

    对于使用 C.C++ 的程序员来说,在内存管理领域,他们既是拥有最高权力的皇帝又是从事最基础工作的劳动人民——拥有每一个对象的“所有权”,又担负着每一个对象生命开始到终结的维护责任.对于 Java 程 ...

    垃圾回收算法手册:自动内存管理的艺术 BOOK

    垃圾回收算法手册:自动内存管理的艺术 2016-03-18 华章计算机 内容简介 PROSPECTUS 本书是自动内存管理领域的里程碑作品,汇集了这个领域里经过50多年的研究沉积下来的最佳实践,包含当 ...

    Xcode 如何设置 自动内存管理 转换为 手动内存管理

    建议使用自动内存管理 ARC. 如果不想自动内存管理,可以在build phases 下的compile sources 中找到不想自动管理的.m文件 ,给它加compiler flags 为 -fn ...

    JVM自动内存管理-Java内存区域与内存溢出异常

    摘要: JVM内存的划分,导致内存溢出异常的可能区域. 1. JVM运行时内存区域 JVM在执行Java程序的过程中会把它所管理的内存划分为以下几个区域: 1.1 程序计数器 程序计数器是一块较小的内 ...

    JVM介绍&amp&semi;自动内存管理机制

    1.介绍JVM(Java Virtual Machine,Java虚拟机) JVM是Java Virtual Machine的缩写,通常成为java虚拟机,作为Java可以进行一次编写,到处执行(Wr ...

    JVM自动内存管理机制——Java内存区域(下)

    一.虚拟机参数配置 在上一篇中介绍了有关的基础知识,这一篇主要是通过一些示例来了解有关虚拟机参数的配置. 1.Java堆参数设置 a) ...

    JVM自动内存管理机制——Java内存区域(上)

    一.JVM运行时数据区域概述 Java相比较于C/C++的一个特点就是,在虚拟机自动内存管理机制的帮助下,我们不需要为每一个操作都写像C/C++一样的delete/free代码,所以也不容易出现内存泄 ...

    随机推荐

    消息队列之ZeroMQ&lpar;C&plus;&plus;&rpar;

    ZMQ是什么? 这是个类似于Socket的一系列接口,他跟Socket的区别是:普通 的socket是端到端的(1:1的关系),而ZMQ却是可以N:M 的关系,人们对BSD套接字的了解较多的是点对点的 ...

    &lbrack;CF733D&rsqb;Kostya the Sculptor(贪心)

    题目链接:http://codeforces.com/contest/733/problem/D 题意:给n个长方体,允许最多两个拼在一起,拼接的面必须长宽相等.问想获得最大的内切圆的长方体序号是多少 ...

    向MyEclipse添加Oracle数据库

    向MyEclipse添加Oracle数据库 1.点击下面圈起来的位置,打开MyEclipse database Explorer视图. 2.在如图空白处右击,选择new进入New Database C ...

    Android学习的一些问题

    如何让Service常驻后台? 如何让App自启动? 如何让App自动更新? Handler Adapter Bundle Application getXXX()

    iOS-SQLite&lpar;FMDB&rpar;

    在已经存在的表中,添加字段,更新表结构 /** Test to see if particular column exists for particular table in database @pa ...

    JSON&period;NET的Self referencing loop detected with type的原因以及解决办法

    模型中有循环引用是很常见的.例如,以下模型显示双向导航属性: : public class Category : { : public Category() : { : Products = new ...

    JSON数据格式转换

    json格式 (JavaScipt Object Notation):是一种数据交换格式!json语法规则:01.对象表现形式 key:value 键值对02.如果有多个数据,之间使用逗号隔开 k1: ...

    python 集合清空

    setp = set(["Red", "Green"]) setq = setp.copy() print(setq) setp.clear() print(s ...

    字典dict常用方法

    字典是列表中常用的方法,我们经常处理字典,字典嵌套,很多复杂的操作都来自于基础,只是改变了样式而已,本质是不变的.下面来看看字典中常用的功能都有那些:     1.clear(self) def cl ...

    展开全文
    weixin_39918248 2021-05-08 00:49:25
  • m0_37415978 2021-05-25 16:45:42
  • weixin_46291251 2021-04-16 21:34:06
  • weixin_42767757 2020-12-22 00:51:45
  • little_rookie__ 2021-01-08 22:06:17
  • qq_38361800 2021-01-11 08:57:31
  • shanwu1 2021-11-04 09:26:35
  • weixin_42297904 2021-05-11 09:38:42
  • qq_40925617 2021-11-09 14:50:57
  • weixin_33437894 2021-05-11 14:24:10
  • weixin_33363025 2021-05-20 15:56:54
  • weixin_52333983 2021-11-19 17:11:47
  • weixin_34010875 2021-03-08 06:00:41
  • weixin_42109925 2021-05-19 17:26:33
  • caozicheng1999 2020-12-23 18:45:03
  • u013712343 2021-03-29 19:45:16
  • qq_35462323 2021-04-07 10:04:42
  • u014099894 2021-02-06 21:24:04
  • m0_50662680 2021-10-19 14:45:59
  • pulledup 2021-05-19 17:33:38
  • qq_43983010 2021-03-12 14:57:11
  • qq_45966440 2021-08-11 18:27:04
  • qq_40989769 2021-01-08 16:33:29
  • qq_44096670 2021-08-24 20:48:49
  • hqy450665101 2021-02-01 23:59:22
  • weixin_44508223 2021-05-23 22:43:56

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 391,496
精华内容 156,598
关键字:

常用的内存管理方法有