页面置换算法总结

页面置换算法

百度百科对页面置换算法给出的定义:在地址映射过程中,若在页面中发现所要访问的页面不在内存中,则产生缺页中断。当发生缺页中断时,如果操作系统内存中没有空闲页面,则操作系统必须在内存选择一个页面将其移出内存,以便为即将调入的页面让出空间。而用来选择淘汰哪一页的规则叫做页面置换算法。

(1)OPT页面置换算法(最佳页面置换算法)
这是一种理想情况下的页面置换算法,但实际上是不可能实现的。该算法的基本思想是:发生缺页时,有些页面在内存中,其中有一页将很快被访问(也包含紧接着的下一条指令的那页),而其他页面则可能要到10、100或者1000条指令后才会被访问,每个页面都可 以用在该页面首次被访问前所要执行的指令数进行标记。最佳页面置换算法只是简单地规定:标记最大的页应该被置换。这个算法唯一的一个问题就是它无法实现。当缺页发生时,操作系统无法知道各个页面下一次是在什么时候被访问。虽然这个算法不可能实现,但是最佳页面置换算法可以用于对可实现算法的性能进行衡量比较。
(2)FIFO页面置换算法(先进先出页面置换算法)
FIFO淘汰算法总是淘汰最先进入内存的页面,即选择在内存中驻留时间最久的页面进行淘汰。该算法实现只需把一个进程已调入内存的页面,按访问的时间先后顺序链接成一个队列,并设置一个指针,该指针始终指向“最老“的页面。

问题:随机一访问串和驻留集的大小,通过模拟程序显示淘汰的页号并统计命中率。示例:

输入访问串:7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1 

驻留集大小:3

7  0  1  2  0  3  0  4  2  3  0  3  2  1  2  0  1 

7  7  7  2  2  2 2  4  4 4  0  0  0  0 0 0 0

    0  0  0 0  3  3 3  2  2  2 2  1  1  1  1

        1  1  1  1  0  0  0  3  3  3  3  3  2  2  2

         7     0 1  2  3  0  4         2  3

红色表示:指针指向调入内存的页面中“最老“的页面

通过模拟程序输出淘汰的页号分别为:7 0 1 2 3 0 4 2 3

命中率为:5/13

注意:内存的页面中“最老“的页面,会被新的网页直接覆盖,而不是“最老“的页面先出队,然后新的网页从队尾入队。

#include 
#include 
#include 
int len;
typedef struct Queue
{
    int  * pBase;
    int front;//队列头
    int rear;//队列尾
}QUEUE;

void init(QUEUE *pQ)
{
    int N = len+1;
    pQ->pBase = (int *)malloc(sizeof(int ) * N);//N为数组长度
    //初始化为0
    pQ->front = 0;
    pQ->rear = 0;
}

int full_queue(QUEUE *pQ)
{
    int N = len+1;
    if((pQ->rear +1)%N == pQ->front)//循环队列
        return 1;
    else
        return 0;
}

int en_queue(QUEUE *pQ, int val)//入队前判断队列是否已满
{
    int N = len+1;
    if( full_queue(pQ) )
    {
        return 0;
    }
    else
    {
        pQ->pBase[pQ->rear] = val;//压栈在队尾
        pQ->rear = (pQ->rear+1) % N;
        return 1;
    }
}

int empty_queue(QUEUE *pQ)
{
    int N = len+1;
    if(pQ->front == pQ->rear)
            return 1;
    else
            return 0;
}

int out_queue(QUEUE *pQ, int *pVal)//出队前判断队列是否为空
{
    int N = len+1;
    if(empty_queue(pQ))
    {
        return 0;
    }
    else
    {
        *pVal = pQ->pBase[pQ->front];//把出队的元素保存起来
        pQ->front = (pQ->front+1)%N;
        return 1;
    }
}

int same_queue(QUEUE *pQ, int x)
{
    int N = len+1;
    int i = pQ->front;
    while( i != pQ->rear)
    {
        if( pQ->pBase[i] == x)
            return 1;
        i = (i+1) % N;
    }
    return 0;
}

int main()
{
    int cnt = 0;//记没命中次数
    int i, val, data;
    char str[1000];

    scanf("%d", &len);//驻留集大小
    scanf("%s", str);

    QUEUE Q;
    init (&Q);

    for(i=0; str[i] != '\0'; i++)
    {
        if(str[i]  >= '0' && str[i] <= '9')
        {
            val= str[i] - '0';
            if( !full_queue(&Q) && !(same_queue(&Q, val)))
            {
                 en_queue(&Q, val);
                 cnt++;
            }
            else if( full_queue(&Q) && !(same_queue(&Q, val)) )
            {
                out_queue(&Q, &data);
                printf("%d ",data);
                en_queue(&Q, val);
                cnt++;
            }
        }
    }
    printf("\n%d/%d", strlen(str)- cnt, strlen(str));
    return 0;
}
(3)LRU页面置换算法(最近未使用页面置换算法)
LRU(Least Currently Used):FIFO总是优先淘汰那些进入内存早的页面,它根据最早进入时间来选择删除某个页面;而恰恰相反,LRU优先淘汰那些经常不使用的页面,它根据最近进入时间来保留某个页面。

问题:随机一访问串和驻留集的大小,通过模拟程序显示淘汰的页号并统计命中率。示例:

输入访问串:7 0 1 2 0 3 0 4 2 3 0 3 2

驻留集大小:3

算法的实现:由于LRU算法淘汰的是上次使用距离t时刻最远的页,故需记录这个距离。

计数器:可使用计数器,给每一个页帧增设一个计数器。每访问一页,就把对应页帧的计数器清零,其余页帧的计数器加1.因此,计数器值为最大的页即上次访问距当前最远的页。

7    0   1    2    0   3    0    4    2    3    0   3    2    

0/7 1/7  2/7  0/2    1/2  2/2  3/2  0/4  1/4   2/4    0/0   1/0 2/0

      0/0 1/0 2/0    0/0 1/0    0/0  1/0  2/0   0/3  1/3   0/3 1/3

             0/1 1/1    2/1  0/3    1/3  2/3  0/2  1/2   2/2   3/2 0/2

缺  缺   缺   缺     命     缺    命   缺   缺     缺    缺    命   命

红色表示:每个页帧对应的计数器值

通过模拟程序输出淘汰的页号分别为:7 1 2 3 0 4

命中率为:4/13

LRU的另一种通俗理解:

例如一个三道程序,等待进入的是1,2,3,4,4,2,5,6,3,4,2,1。先分别把1,2,3导入,然后导入4,置换的是1,因为他离导入时间最远。然后又是4,不需要置换,然后是2,也不需要,因为内存中有,到5的时候,因为3最远,所以置换3,依次类推


#include 
#include 
#include 
int len;

typedef struct LRU
{
    int data;
    int time;//计次数
} LRU;

typedef struct Queue
{
    LRU *pBase;//结构数组
    int front;//队列头
    int rear;//队列尾
}QUEUE;

void init(QUEUE *pQ)
{
    int N = len+1;
    pQ->pBase = (LRU*)malloc(sizeof(LRU ) * N);
    pQ->front = pQ->rear = 0;  //初始化为0
}

int full_queue(QUEUE *pQ)
{
    int N = len+1;
    if((pQ->rear +1)%N == pQ->front)//循环队列
        return 1;
    else
        return 0;
}

int en_queue(QUEUE *pQ, int val)//入队前判断队列是否已满
{
    int N = len+1;
    if( full_queue(pQ) )
    {
        return 0;
    }
    else
    {
        pQ->pBase[pQ->rear].data = val;//压栈在队尾
        pQ->pBase[pQ->rear].time = 0;//初始化次数为0
        pQ->rear = (pQ->rear+1) % N;
        return 1;
    }
}

int empty_queue(QUEUE *pQ)//1-->空   0-->非空
{
    int N = len+1;
    if(pQ->front == pQ->rear)
            return 1;
    else
            return 0;
}

int out_queue(QUEUE *pQ, int *pVal)//出队前判断队列是否为空
{
    int N = len+1;
    if(empty_queue(pQ))
    {
        return 0;
    }
    else
    {
        *pVal = pQ->pBase[pQ->front].data;//把出队的元素保存起来
        pQ->front = (pQ->front+1)%N;
        return 1;
    }
}

void add_time(QUEUE *pQ)
{
    int N = len+1;
    int i = pQ->front;
    while( i != pQ->rear)
    {
        pQ->pBase[i].time ++;
        i = (i + 1) % N;
        //printf("%d  %d", pQ->pBase[i].time, i);
    }
}

void Set_time_shot(QUEUE *pQ, int x)//若待入队元素与队中元素相同,将次数置为0
{
    int N = len + 1;
    int i = pQ->front;
    while( i != pQ->rear)
    {
        if( pQ->pBase[i].data == x)
        {
            pQ->pBase[i].time = 0;
        }
        i = (i+1) % N;
    }
}

int Find_big_time(QUEUE *pQ)
{
    int N = len + 1;
    int i = pQ->front;
    int max_i = i;
    int max_time = pQ->pBase[pQ->front].time;
    while( i != pQ->rear)
    {
        if( pQ->pBase[i].time > max_time)
        {
            max_i = i; max_time = pQ->pBase[i].time;
        }
        i = (i+1) % N;
    }
    return max_i;
}

void Replace_big_time(QUEUE *pQ, int x)//若待入队元素与队中元素不相同,替换元素,并将次数置为0
{
    int max_time = Find_big_time(pQ);
    printf("%d ", pQ->pBase[max_time].data);
    pQ->pBase[max_time].data = x;
    pQ->pBase[max_time].time = 0;
}

int same_queue(QUEUE *pQ, int x)//判断待入队元素是否与队中元素相同
{
    int N = len+1;
    int i = pQ->front;
    while( i != pQ->rear)
    {
        if( pQ->pBase[i].data == x)
            return 1;
        i = (i+1) % N;
    }
    return 0;
}

int main(void)
{
    char str[100];
    int val, data;
    int i, cnt = 0;
    scanf("%d", &len);
    scanf("%s", str);

    QUEUE Q;
    init(&Q);
    for(i=0; str[i] != '\0'; i++)
    {
        val = str[i] - '0';
        if ( empty_queue( &Q ) )//如果队列为空
        {
            en_queue(&Q, val);
        }
        else
        {
                add_time(&Q);
                if(full_queue(&Q))//如果队列已满
                {
                        if( !same_queue(&Q, val))
                        {
                            Replace_big_time(&Q, val);
                        }
                        else
                        {
                            Set_time_shot(&Q, val);
                            cnt++;
                        }
                  }
                 else//如果队列没满也不为空
                  {
                        if( !same_queue(&Q, val))
                        {
                            en_queue(&Q, val);
                        }
                        else
                        {
                            Set_time_shot(&Q, val);
                            cnt++;
                        }
                  }
            }
    }
    printf("\n%d/%d", cnt, strlen(str));
    return 0;
}

注意: 虽然两个算法都是用队列这种数据结构实现的,但具体操作不完全遵从队列的原则。这一点不必纠结。
命中率是指在队满的情况下,新的元素的加入,不影响队列其它元素。即该元素已存在在队列中。
(4)LFU页面置换算法(最少使用页面排序算法)
LFU(Least Frequently Used):内存内使用越频繁的页面,被保留的时间也相对越长。

问题:
哈尔滨工业大学2000年考研操作系统真题操作系统考研辅导教程87页一请求分页存储系统,进程P共有5页,访问串为3,2,1,0,3,2,4,3,2,1,0,4,试采用LRU算法和LFU算法分析,当分配页面数为3时,发生的缺页中断次数。
解:
least frequently used (LFU),要求在页置换时置换引用计数最小的页。
	3,2,1
	0进入时,缺页。置换最近最小的1。内存:3,2,0
	3,2
	4进入时,缺页。置换最近最小的0。内存:3,2,4
	3,2
	1进入时,缺页。置换最近最小的4。内存:3,2,1
	0进入时,缺页。置换最近最小的1。内存:3,2,0
	4进入时,缺页。置换最近最小的0。内存:3,2,4


你可能感兴趣的:(C&C++,Algorithm,Operating,System)