操作系统内存的总结

1、内存的结构

cpu的寄存器-》cpu和内存之间的cache-》内存-》内存和硬盘之间的cache-》硬盘

因为cpu的运算和处理速度非常快,远远大于内存的读取速度,所以cpu要处理的数据存在寄存器上,寄存器的读取速度和cpu同步但价格昂贵,所以寄存器和内存之间有一层cache缓存,磁盘和内存之间同理。

磁盘和内存之间的缓存由内存提供,寄存器和内存之间的缓存是单独一层。

2、内存的分配方式

1)连续的内存分配

  (1)单一内存连续分配

  (2)固定分区分配

  (3)动态分区分配

     动态分区分配的结构:

      1)空闲分区表:一张表记录了所有空闲分区的情况

      2)空闲分区链:双向链表连接了所有的空闲分区,链尾有标记位,记录是否空闲

     动态分区分配算法:

      1)首次适应算法       

        每次从链首开始查找,找到一块能放下的空间放入,多的空间继续留用

        优点:能在链尾留下一些大空间

        缺点:首部分会留下很多小的内存碎片,每次从首部开始遍历,浪费时间

      2)最佳适应算法   

        把空间按大小排序,每次从最大的空间开始排,如果放的下就放入

        优点:速度快,留下的内存碎片少

        缺点:不适合大文件的放入,因为优先把大空间用掉了

      3)最坏适应算法     

        把空间从小到大排序,然后遍历链表,找到第一个能放进去的空间放进去

        优点:每次放入最合适的空间,保留大空间

        缺点:留下很多小的内存碎片

      4)最快适应算法      

        为每个分区做一个索引,记录了分区的大小和位置,每次直接匹配最合适的空间,而且不对空闲区造成分割,保留大空间同时不产生碎片

        优点:速度快,迅速找到合适的内存放入,不对分区分割,不产生碎片

        缺点:归还复杂,开销大。每个分区只有一个进程,浪费空间。

    内存的回收:如果附近有空闲区域,则和该区域合并,选一个首地址,如果周围不存在空闲区,则为其建单独表项

    内存碎片的合并:

      1)合并碎片,把程序紧凑在一起,移动的过程中程序要中断

      2)寄存器重定位,将几个零碎的碎片用一个寄存器记录,每次用完上段,通过寄存器定位下段内存地址,这样实现了零碎内存的利用

  固定分配限制了进程的数量,而且降低了内存的利用率,动态分配算法复杂,回收空闲分区合并时,系统开销很大

2)离散内存

  (1)页式管理

      将一个进程的逻辑地址空间分成若干个大小相等的页。每个分页包括一个页号和偏移量,由一个统一的页表管理。

      将逻辑地址转换为物理地址,通过一个寄存器实现,每个页表对应一个寄存器,为了节约寄存器数量,可以用多级页表管理。

      优点:没有外碎片,内碎片大小不超过页的大小。

      缺点:程序全部装入内存,要求有相应的硬件支持。

  (2)段式管理

    段式管理把程序按内容或者过程函数关系划分成段,每段有自己的名字。一个用户作业或者进程所包含的段对应一个二维线性虚拟空间。

    优点:可以分别编写和编译,针对不同类型的段可以采取不同的保护,还能对一些段实现内存共享。

    缺点:产生碎片较多。

  (3)段页式管理

    系统为每个作业或者进程简历一张段表以管理内存分配和释放,缺段处理,另外由于一个段又被分为若干页,每个段还要简历一张页表把段中的虚页变换成内存中实际的页面。

    有了页式和段式管理的优点,但是程序的复杂性和开销也增加了,使得执行速度下降。

3、虚拟内存

虚拟内存的概念:没必要每次把一个作业的所有信息放到内存里,可以把用到的放到内存里,没用到的放到虚拟内存里,需要用的时候再置换

硬件支持:分页请求,分段请求,段页式请求,需要分页,分段的硬件

内存分配策略:

  (1)固定分配,局部置换

    每个作业分配一样的物理内存,不够的时候,置换该内存里的资源

  (2)可变分配全局置换

    每个作业不固定分配,用多少分配多少,不够的时候随机找空闲的,替换掉虚拟内存里要用的数据

  (3)可变分配局部置换

    每个作业初始固定分配一部分内存,不够置换虚拟内存里的,缺页率大就加物理内存分配,缺页率少的就减少物理内存分配

页面置换算法:

  (1)最佳置换算法(Optimal)

    理想情况,不可能实现,将内存里未来最长时间要访问的数据和虚拟内存里要用的数据置换

  (2)先进先出算法(FIFO)

    将先进内存的数据先和虚拟内存里要用的数据替换

  (3)LRU算法

    将最近的数据里最久未被使用的和虚拟内存里的数据替换

     1)寄存器的实现

       为每个页面配一个移位寄存器,比如8位的,每次读取就是1000000,然后每次读取数据不是本数据就向右移动一位,最后需要置换的时候,置换最小的那个数据

     2)栈的实现

       将读到的数据移动到栈顶,每次需要调换的时候,调换栈底的数据。

  (4)简单Clock置换算法

      把内存里所有的页面用指针连成一个循环队列,每次读到的时候设为1,没读到设为0,如果指针指到0的,就置换。等于是FIFO和LRU的简化结合。

  (5)复杂Clock置换算法

      考虑到内存如果已经被修改,放到磁盘的时候要重新写入,消耗更大。所以判断增加了一个评级。

      替换优先级-》未被访问且未修改的优先替换,然后是未被访问且修改,然后是访问未被修改,然后是访问且修改。

  (6)最少使用置换算法(LFU)

      用移位寄存器记录使用频率。将使用频率最低的优先置换。

4、程序如何装进内存

代码链接以后,先装入模块,再装入程序,最后放进内存里

1)动态链接

动态链接分两种,装入时动态链接和运行时动态链接

装入时动态链接是dll,在程序装入内存时放到内存里

运行时动态链接,在运行时用到了再去查找,这样平时更省内存,但用到的时候效率更低

2)静态链接

静态链接是lib,在装入模块的时候装入,然后装进程序里,再装入内存,这样程序比较大,也耗内存

5、为什么要内存对齐,如何实现new的时候以某字节对齐

内存对齐就是要求数据的首地址要是某个特定的值

       1、平台原因(移植原因)
             A   不是所有的硬件平台都能访问任意地址上的任意数据的;
             B    某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
        2、性能原因:
             A   数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
             B   原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

6、C++如何new的时候不分配内存但调用构造函数

详见:https://www.jianshu.com/p/b52a5df69c88

placement new,先申请一个对象需要的空间,然后new对象的时候在new后面带一个括号,括号里面写入申请的空间,然后new就只调用构造函数,然后将对象放入事先申请的空间

对于频繁构造和析构的情况,用placement new事先申请空间,然后new对象,然后调用对象析构函数保留空间,不用频繁申请空间,比正常的new效率高很多

7、伙伴系统

对内存的分配和释放大小永远是2的次方,方便合并和查找可用内存,效率很高,尤其是在多机系统。

猜你喜欢

转载自www.cnblogs.com/yiwufang/p/9563549.html