使用链表的例子:页帧管理
在一些支持虚拟内存的系统中有关于链表的一种应用。虚拟内存是一种地址空间的映射机制,它允许进程不必完全加载到物理内存(系统的实际内存)中也可以得到运行。这种方式的一个优点是进程可以使用比系统实际所允许的物理内存大得多的地址空间。另一个优点是多个进程能够共享系统的内存以并发的方式执行。
运行在虚拟内存机制下的进程需要处理虚拟地址。
这些地址对于进程来说就像是物理地址一样,但使用前必须由操作系统转换。采用由专门的硬件所支持的页表来快速执行地址转换工作。
每一个进程都有它自己的页表,将它的虚拟地址空间中的页映射到物理内存的页帧上。
当某个进程引用一个虚拟地址时,页表中的某项需要检查并决定该页关联到哪个物理页帧上(图1)。
当进程引用一个不在物理页帧上的虚拟地址时,会导致系统产生一个页错误并为之在物理内存中分配一个页帧。为什么进程的页面会从物理内存中移除是另一个问题。一种导致进程页被移除的场景是:当访问某个页面的频率同其他页面相比很低时,而且在别处用到该页帧的情况下。
下面这个例子就是针对页帧管理所设计的。
为此,将介绍两个函数:alloc_frame和free_frame(见示例1)。alloc_frame和free_frame采用链表来维护可供分配的页帧。
函数alloc_frame从空闲页帧链表中获取空闲页帧号。给定某个特定的页,将页帧号放到页表中来检查该页面应该对应哪个物理页帧。
一旦某个页面从物理内存中移除后,函数free_frame接受一个页帧号并将其放到空闲面帧链表中。这两个函数假定在执行之前,操作系统已经将所有的空闲页帧都插入空闲页帧链表中了。
用链表来管理页帧是一种非常好的方法,因为页帧的分配将涉及频繁的插入和删除操作,而且这些操作都发生在链表头。
alloc_frame和free_frame的运行复杂度都是O(1),因为这两个函数都分别只是简单的调用list_ints_next以及list_rem_next,而这两个函数在单链表中已经分析过了,它们都是复杂度为O(1)的操作。
示例1:页帧管理的函数实现
/*frames.c*/
#include <stdio.h>
#include "frames.h"
#include "list.h"
/* alloc_frame */
int alloc_frame(List *frames)
{
int frame_number,*data;
if(list_size(frames)==0)
/*Return that there are no frames available. 没有空闲的页帧号*/
return -1;
else
{
if(list_rem_next(frames,NULL,(void**)&data)!=0)
/*Return that a frame cloud not be retrieved. 不能检索页帧*/
return -1;
else
{
/*Store the number of the available frame. 存储可用页帧号*/
frame_number = *data;
free(data);
}
}
return frame_number;
}
/*free_frame*/
int free_frame(List *frames,int frame_number)
{
int *data;
/*Allocate storage for the frame number. 为闲置的页帧号分配空间*/
if((data=(int *)malloc(sizeof(int)))==NULL)
return -1;
/*Put the frame back in the list of available frames. 将页帧放回页帧链表*/
*data=frame_number;
if(list_ins_next(frames,NULL,data)!=0)
return -1;
return 0;
}