操作系统基础内存知识

之前每次看操作系统的内存,看完就忘记了,现在借着博客记录下来,内存这一块,平常应用层编程中可能不需要理解很深,底层设计时是不可避免的。

无抽象存储:

每一个设计总是针对不同的问题,早期存储器没有抽象,每一个程序可以直接访问内存,这种情况下,内存不能同时放两个程序,因为容易互相干扰到。

当然,可以通过对内存分成每一块,利用特殊的硬件,每一块用保护键避免对其他程序的干扰。但这时又会产生一个问题,用户只能使用绝对物理内存,我们并不知道自己的应用程序会放在哪块内存中,再真正跑起来,用户程序一旦越界代码就崩溃了。

静态重定位:

这时候有人就提出了一种技术叫“静态重定位”,它的做法就是在程序装载到内存的时候,把该程序所放置的内存地址加到每一个程序地址上。虽然这种办法不出错时可行,但是容易出错,会减慢装载速度,且要求可执行程序提供额外信息区分内存字中哪些存有(可重定位的)地址,哪些没有,装载器需要一定的方法来辨别地址和常熟。因此,这不算OK的解决办法。

地址空间:

上面都是直接使用物理地址,使用这种方法会带来几个严重问题:1.如果用户程序可以寻址内存的每个字节,他们就可以很容易地破坏操作系统,即使只有一个程序在运行,这种问题也很容易存在;2.这种模型,想要同时运行多个程序非常困难;

为此,我们需要解决两个问题:保护和重定位。为此,一个更好的办法是,创造新的内存抽象:地址空间。每个进程都有自己的地址空间,这个地址空间独立于其他进程的地址空间。那么这里比较难的是,如何使得一个程序中的一个地址如1000所对应的物理地址不同于另一个程序的地址1000所对应的物理地址。

动态重定位:

一个简单办法是一种简单的重定位:经典办法是给CPU配置两个特殊硬件寄存器,通常叫:基址寄存器和界限寄存器。当使用这两个寄存器时,程序装载到内存中连续的空闲位置且装载期间无须重定位。当一个程序运行时,程序的起始物理地址装载到基址寄存器中,程序的长度装载到界限寄存器中。

每次程序访问内存,取一条指令,读或写一个数据字,CPU硬件会把地址发送到内存总线前,自动把基址值加到进程发出的地址上,同时,它检查程序的地址是否等于或大于界限寄存器里的值。

这种基址寄存器和界限寄存器重定位的缺点是:每次访问内存都需要进行加法和减法运算。比较可以做的很快,但是加法由于进位传递时间的问题,在没有使用特殊电路的时候会显得很慢。

交换技术:

因为计算机物理内存总是不能够大到保存所有进程。所有进程所需的RAM数量总和通常要远远超出存储器所能够支持的范围。这时就需要交换技术来处理内存超载。

1.最简单的策略是交换技术,即把一个进程完整调入内存,使该进程运行一段时间,然后把它存回磁盘。空闲进程主要存在磁盘上,所有当它们不运行时就不会占用内存。

2.另一种策略就是虚拟内存,该策略能使程序只有一部分被调入内存的情况下运行。


简单的交换技术:

简单的总是一整块进程调入调出内存。交换在内存中产生了很多空闲区(也称为空洞),通常把所有的进程尽可能向下移动,有可能将这些小的空闲区合成一大块。该技术称为内存紧缩。

但是这个操作一般不进行,因为要耗费大量CPU时间。

当进程创建或换入时应该分配多大 的内存,因为进程的数据段是可以增长的(很多语言允许从堆中动态分配内存),当进程空间试图增长,就会出现问题。这时经常需要把增长的进程移到内存中一个足够大的区域中去。两种办法:1.当换入或移到进程时为它分配一些额外的内存,当进程被换出到磁盘上时,应该只交换进程实际上使用的内存中的内容。2.如果进程有两个可增长的段,例如,供变量动态分配和释放的作为堆使用的一个数据段,以及存放普通局部变量与返回地址的一个堆栈段,可以使这两者之间的内存供两个段使用,堆栈段从顶端向下增长,数据段向上增长。

如果分配的额外的内存用完了,进程或者必须移动到更足够大 的空闲区中(也可以交换出内存直至内存有足够的空间),或者结束该进程。


空闲的内存管理:

动态分配内存时,操作系统必须对其进行管理。有两种方式跟踪内存使用情况:位图和空闲链表。

位图:

使用位图时,内存可能被划分成小到几个字或大到几千个字节的分配单元。每个分配单元对应位图中的一位,0表示空闲,1表示占用。分配单元的大小是一个重要的设计,若选择比较大的分配单元,则位图更小。

位图提供了一种简单的利用一块固定大小的内存区就能对使用情况进行记录的方法。这种方法主要问题是:在决定把一个占K个分配单元的进程调入内存时,处理器必须搜索位图,在位图中找到有k个连续的0串,这个查找很耗时,是位图的缺点。

使用链表的存储管理:

一个是一条链表,记录已分配的内存段和空闲的内存段,通过每个节点上记录空闲区(H)、进程(P)的指示标志、起始地址、长度和指向下一个节点的指针。

一种是用双链表,一个存分配的,一个存空闲的。这样方便些。

当按照地址顺序在链表中存放进程和空闲区时,有几种算法可以用来为创建的进程分配内存:

1.首次适配法:沿着段链表找到一个足够大的空闲区,除非空闲区大小和要分配的空间大小一样大,否则将该空闲区分为两部分,一部分供进程使用,另一部分形成新的空闲区。首次适配方法是一种速度很快的算法。

2.最佳适配算法:搜索整个链表,找出能够容进程的最小空闲区。以最好地匹配请求和可用空闲区,而不是拆分一个以后可能会用到的大的空闲区。

最佳适配方法会分裂出非常小的空闲区,为了避免这一问题,可用使用最差适配算法,总是分配最大的,使新分裂的可用继续使用。

最佳适配算法比首次适配或下次适配会浪费更多的内存,因为产生了大量无用的小空闲区。


该方面总结主要参考现代操作系统。最后,对于虚拟内存,将放到下一章单独写出来,想结合linux这一块的知识。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值