1. 局部分配策略与全局分配策略
局部页面置换算法:为每个进程分配固定的内存片段,每个进程页框数固定。
全局页面置换算法:在可运行进程之间动态分配页框,分配给每个进程的页框数是随时间变化的。
一般情况下,全局页面置换算法比较好,当工作集的大小随进程运行时间变化明显时更加明显。如此时使用局部页面置换算法,即使有大量空闲页面在,也可能会颠簸,工作集变小时,每个进程都会有大量的页框被闲置,造成内存浪费。使用全局算法时,系统必须随时确定应该给进程分配的页面多少,一种实现是用LRU的老化位监测工作集大小,然后这并没有什么卵用,因为,老化算法是根据时间滴答来确定页面集大小,间隔太长,工作集的大小往往几十微秒就波动很大了。
法1,定期确定进程数目,为其分配等额的页面,然而并不公平,有的进程需要多,有的需要少。
法2,根据进程的大小按比例为其分配页面。动态更新比例。管理动态内存的一种方法就是PPF,即缺页中断率算法,计算每秒的缺页中段数,当某个进程的缺页中断率高时,为他分配,低时剥夺页面。使得每个进程都保持在可接受范围内。
既适用于全局也适用于局部的算法有:LRU,FIFO
只适用于局部的是时钟集算法和工作集算法。
2. 负载控制
与分配算法无关,只要所有进程需要的页框数总和超过了内存容量,就可能发生颠簸。
原因是此时一些进程需要更多页面,但是没有更少的进程需要更少的内存,唯一的办法就是挂起某些进程,使用交换技术把内存交换到磁盘上去,以便让其它进程减小压力。
交换时要考虑进程是I/O密集型的还是CPU密集型的,以免CPU空闲。
3. 页面大小
页面大小的考虑以下元素:1.内部碎片,正文段、数据段可能填不满一个页面。剩余的就被浪费了。
2.更小的页面需要更大的页表,而内存与磁盘的传输是一次一页。传输速度会变慢,另外,某些机器上每次CPU从一个机器切换到另一个进程时需要把新进程的页表装入硬件寄存器,速度变慢。
分离的指令空间和数据空间
大多数计算机只有一个地址空间,既存放程序也存放数据,当地址空间太小时,会很困难了。于是PDP-11就提出一种方案:为程序正文和数据设置两个空间,I空间和D空间。链接器必须知道何时使用I空间和D空间,使用他们时,数据会被重定位到虚拟地址0,而不是程序之后开始。
共享页面
多道程序设计系统中,一个程序经常被多个用户运行,此时共享页面就很有用了,
如果机器支持I空间和D空间,就很简单,各个共享程序文段的进程使用相同的I空间不同的D空间页表就可以实现共享。每个进程在它的进程表中都有看个指针,一个指向I空间,一个指向D空间页表,使用这些指针来定位合适的页表。并使用他们来设立MMU。
假设进程A和进程B共享代码时,进程A结束时,调度程序决定从内存移走A的所有页面,于是要撤销,然而B就会出现大量缺页中断。因而需要专门的数据结构记住共享页面。
共享数据比共享程序麻烦些,在UNIX中,在进行fork系统调用时,父进程与子进程要共享程序文本和数据,在分页系统中,通常是让这些进程分别拥有自己的页表,但是都指向一个页面集合,这样fork时就不必复制页面,此时两个进程中所有页面都是只读的,此后只要一点数据被改变了,就会触发只读保护,引发陷阱。随后生成该页的副本。这样每个进程都有自己的专用副本。这叫写时复制。可减少复制。
共享库
1.静态绑定库,现代操作系统,很多大型库被众多进程使用。将所有库静态的与磁盘上的每一个可执行程序绑定在一起,会使他们更庞大。
2.共享库(windows称为DLL或动态链接库)
现在考虑传统链接库的方法
Linux中是这样的指令:ld * .o –lc–lm 这个命令的过程是这样的,链接当前目录下所有的.o文件,并扫描两个库:/usr/lib/libc.a和/usr/lib/libm.a。任何在目标文件中调用了但是没有被定义的函数(比如printf),都被称作未定义外部函数。链接器会在库中寻找这些未定义外部函数,若找到,把它们加载到可执行二进制文件中,任何被这些未定义外部函数调用了但是不存在的函数也会成为未定义外部函数,如printf中需要write,如果write没被加载进来,链接器就会查找write并把它加载进来,当链接器完成任务时,一个可执行二进制文件被写到磁盘,此时这个文件已经包含了所需的全部函数。在库中但是没有被定义的不会包含。当程序装入内存中使用时,所有函数已经就绪了。
假设普通程序需要消耗20~50M用于图形和用户界面函数,静态链接上百个包括这些库的程序会浪费大量的磁盘空间,装载这些程序时也会浪费大量的内存空间。因为系统不知道它可以共享这些库,这就是引入共享库的原因。系统只会呆板的一遍一遍执行链接操作。
当一个程序与共享库有链接时,链接器不加载被调用的函数,而是加载了一小段能在运行时绑定的被调用函数的存根例程(stub routine)。依赖于系统和配置信息,共享库或者和程序一起被装载,或者在其所包含函数第一次被调用时被装载,如果其他程序已经装载了某个共享库,这个共享库就不会再次被装载,这样就避免了多次装载。当一个共享库被装载并使用时,整个库并不是被一次性的读入内存,而是根据需要,以页面为单位装载。因此没有调用到的函数是不会被装载到内存中的。
共享库优点:1.使可执行文件更小,节省内存空间。2.如果共享库中的一个函数有bug,那么不需要编译整个程序,只要更新DLL文件就可以!
共享库带来的小问题,若两个进程共享一个库,那么共享库可能会被定位到不同的地址上,而由于库共享,无法使用重定位。解决方法:编译共享库时,告诉编译器,不要产生使用绝对地址的指令,不论共享库放置在虚拟位置地址空间的什么位置,都可以正常工作。即位置无关代码。
3.内存映射文件
共享库是一种内存映射文件的特例。其核心思想是:进程可以通过发起一个系统调用,将一个文件映射到其虚拟地址空间的一部分,在大多数实现中,在映射共享的页面不会实际读入页面的内容,而是在访问页面时才会每次一页的读入,磁盘文件则被当做后备存储。当进程退出或显式的解除文件映射时,所有被改动的页面都会写回到文件。
内存映射文件提供了一种I/O的可选模型,可以把一个文件当做一个内存中的大字符数组来访问,而不用通过读写操作来访问这个文件。
如果两个或两个以上的进程同时映射了同一个文件,他们就可以通过共享内存来通信,一个进程在共享内存上完成了写操作,此刻当另一个进程在映射到这个文间的虚拟地址空间上执行读操作时,他就立即可以看到上一个进程写操作的结果,其实这就相当与一个进程之间的高带宽通道。
4. 清除策略
如果发生缺页中断系统中有大量的空闲页框,此时分页系统工作在最佳状态,如果每个页框都被占用,而且修改过话,再换入一个新页面时,旧页面应首先写回磁盘,为保证有足够的空闲页框,分页系统会有后台进程:后台守护进程,他大多数时候睡眠,定期被唤醒以检查内存的状态,如果空闲页框过少,分页守护进程通过预先的页面置换算法选择页面换出内存。如果这些页面被修改过,则写回磁盘,
任何情况下,页面中原先的内容都被记录下来,但需要使用一个已被淘汰的页面时,如果该页框没有被覆盖,就把他从空闲页面缓冲池移出,保存一定数量的页框供给比效能比使用所有内存并在需要的时候搜索一个页框有更好的性能,分页守护进程至少保证所有的空闲页框都是干净的,在分配时不必急着写回磁盘。
实现清除策略的一种方法是:使用一个双指针时钟,当前指针由分页守护进程控制,当它指向一个脏页面时,就把页面写回磁盘,前指针移动,后指针用于页面置换,就像在标准时钟算法一样。这样后指针命中干净页面的概率会增大。
5. 虚拟内存接口
对于高级系统,允许程序员对内存映射进行控制。
这样做的原因之一就是为了使多个进程共享同一部分内存。如果我们可以对内存区域命名,让一个进程把这片区域告诉另一个进程,第二个进程就可以把这片区域映射到自己的虚拟空间,从而实现高带宽的共享。
页面共享在消息传递的功能:进程控制他们的页面映射。发送进程清除对包含消息的信息映射,接受进程映射进来,这样避免了复制全部信息,只要复制页面的名字。