Linux操作系统实验 —— 模拟请求页式存储管理系统

本文详细描述了一个实验,通过编程模拟理解请求页式存储管理系统中FIFO、LRU和CLOCK三种页面替换算法的工作原理、性能特点,以及如何在给定的内存限制下处理页面调度。实验结果展示了这些算法在特定访问模式下的性能差异和适用性。
摘要由CSDN通过智能技术生成

一  实验目的

(1)理解虚拟内存管理的原理和技术。

(2)理解不同页面替换算法的工作原理和性能特征。

(3)掌握并实现请求页式存储管理中常见的页面替换算法。

二  实验内容

2.1 任务一

   本实验要求编程模拟请求页式存储管理系统中的进程运行情况。一个进程被分配了若干个物理块,运行时按照给定的页面访问序列进行操作。在发生缺页中断时,将使用以下页面替换算法进行页面置换:

  1.先进先出算法(FIFO)

  2.最近最少使用算法(LRU)

  3.CLOCK 算法(即第二次机会算法)

    假设该进程被分配了 4 个内存物理块,按照顺序访问 10 个页面,具体访问序列为:3、6、2、1、3、3、7、6、1、4。要求编写 C 程序,分别实现上述三种页面调度算法,程序输入包括内存物理块数量、页面访问序列;输出包括当前驻留在内存区的页面,缺页次数,页面置换算法的命中率(即页面命中次数/访问页面数)。

2.2 任务二

    除提供的示例外,可以自行修改物理块分配个数以及访问序列,检验实现的页面置换算法的有效性和稳定性。

三  实验过程及结果

(1)编程模拟请求页式存储管理系统中的进程运行情况。一个进程被分配了若干个物理块,运行时按照给定的页面访问序列进行操作。在发生缺页中断时,将使用三种不同的页面替换算法进行页面置换。

【实验步骤】

1. 在用户主目录下创建一个demo目录,存放本次实验的代码文件,进入demo目录

2. 在demo目录下创建test.c文件,编写代码

#include <stdio.h>
#include <stdlib.h>

// 页面结构
typedef struct {
    int page_number;
    int referenced;  // 用于LRU算法
} Page;

// 页面调度算法类型
typedef enum {
    FIFO,
    LRU,
    CLOCK
} PageReplacementAlgorithm;

// FIFO算法
void fifo(Page pages[], int page_sequence[], int page_count, int max_frames);

// LRU算法
void lru(Page pages[], int page_sequence[], int page_count, int max_frames);

// CLOCK算法
void clock_algorithm(Page pages[], int page_sequence[], int page_count, int max_frames);

// 打印当前内存状态
void print_memory_state(Page pages[], int frame_count);

int main() {
    int max_frames;
    printf("Enter the number of physical memory blocks: ");
    scanf("%d", &max_frames);

    int page_count;
    printf("Enter the number of pages in the sequence: ");
    scanf("%d", &page_count);

    int *page_sequence = (int *)malloc(page_count * sizeof(int));

    printf("Enter the page sequence: ");
    for (int i = 0; i < page_count; i++) {
        scanf("%d", &page_sequence[i]);
    }

    Page *pages = (Page *)malloc(max_frames * sizeof(Page));

    printf("\nFIFO Algorithm:\n");
    fifo(pages, page_sequence, page_count, max_frames);

    printf("\nLRU Algorithm:\n");
    lru(pages, page_sequence, page_count, max_frames);

    printf("\nCLOCK Algorithm:\n");
    clock_algorithm(pages, page_sequence, page_count, max_frames);

    free(page_sequence);
    free(pages);

    return 0;
}

void fifo(Page pages[], int page_sequence[], int page_count, int max_frames) {
    int frame_count = 0;
    int page_faults = 0;
    int next_frame_index = 0;

    for (int i = 0; i < page_count; i++) {
        int page_number = page_sequence[i];

        // 检查页面是否已经在内存中
        int page_found = 0;
        for (int j = 0; j < frame_count; j++) {
            if (pages[j].page_number == page_number) {
                page_found = 1;
                break;
            }
        }

        // 页面不在内存中,发生缺页中断
        if (!page_found) {
            // 检查内存是否已满
            if (frame_count < max_frames) {
                // 内存未满,直接加入
                pages[next_frame_index].page_number = page_number;
                next_frame_index = (next_frame_index + 1) % max_frames;
                frame_count++;
            } else {
                // 内存已满,使用FIFO算法置换页面
                pages[next_frame_index].page_number = page_number;
                next_frame_index = (next_frame_index + 1) % max_frames;
            }
            page_faults++;
        }

        // 打印当前内存状态
        print_memory_state(pages, frame_count);
    }

    // 打印结果
    printf("Page Faults: %d\n", page_faults);
    printf("Page Hit Ratio: %.2f%%\n", (1 - (float)page_faults / page_count) * 100);
}

void lru(Page pages[], int page_sequence[], int page_count, int max_frames) {
    int frame_count = 0;
    int page_faults = 0;
    int next_frame_index = 0;

    for (int i = 0; i < page_count; i++) {
        int page_number = page_sequence[i];

        // 检查页面是否已经在内存中
        int page_found = 0;
        for (int j = 0; j < frame_count; j++) {
            if (pages[j].page_number == page_number) {
                page_found = 1;
                // 更新页面的referenced字段
                pages[j].referenced = i;
                break;
            }
        }

        // 页面不在内存中,发生缺页中断
        if (!page_found) {
            // 检查内存是否已满
            if (frame_count < max_frames) {
                // 内存未满,直接加入
                pages[next_frame_index].page_number = page_number;
                pages[next_frame_index].referenced = i;
                next_frame_index = (next_frame_index + 1) % max_frames;
                frame_count++;
            } else {
                // 内存已满,使用LRU算法置换页面
                int min_referenced = pages[0].referenced;
                int min_index = 0;
                for (int j = 1; j < frame_count; j++) {
                    if (pages[j].referenced < min_referenced) {
                        min_referenced = pages[j].referenced;
                        min_index = j;
                    }
                }
                pages[min_index].page_number = page_number;
                pages[min_index].referenced = i;
            }
            page_faults++;
        }

        // 打印当前内存状态
        print_memory_state(pages, frame_count);
    }

    // 打印结果
    printf("Page Faults: %d\n", page_faults);
    printf("Page Hit Ratio: %.2f%%\n", (1 - (float)page_faults / page_count) * 100);
}

// CLOCK算法
void clock_algorithm(Page pages[], int page_sequence[], int page_count, int max_frames) {
    int frame_count = 0;
    int page_faults = 0;
    int hand = 0;  // 时钟算法的指针

    for (int i = 0; i < page_count; i++) {
        int page_number = page_sequence[i];

        // 检查页面是否已经在内存中
        int page_found = 0;
        for (int j = 0; j < frame_count; j++) {
            if (pages[j].page_number == page_number) {
                page_found = 1;
                break;
            }
        }

        // 页面不在内存中,发生缺页中断
        if (!page_found) {
            // 检查内存是否已满
            if (frame_count < max_frames) {
                // 内存未满,直接加入
                pages[frame_count].page_number = page_number;
                pages[frame_count].referenced = 1;  // 新加入的页面设置referenced为1
                frame_count++;
            } else {
                // 内存已满,使用CLOCK算法置换页面
                while (1) {
                    if (pages[hand].referenced == 0) {
                        // 找到第一个未被引用的页面
                        pages[hand].page_number = page_number;
                        pages[hand].referenced = 1;  // 替换的页面设置referenced为1
                        break;
                    } else {
                        // 将页面的referenced字段清零
                        pages[hand].referenced = 0;
                    }
                    hand = (hand + 1) % max_frames;
                }
            }
            page_faults++;
        } else {
            // 页面已经在内存中,设置页面的referenced字段为1
            for (int j = 0; j < frame_count; j++) {
                if (pages[j].page_number == page_number) {
                    pages[j].referenced = 1;
                    break;
                }
            }
        }

        // 打印当前内存状态
        print_memory_state(pages, frame_count);
    }

    // 打印结果
    printf("Page Faults: %d\n", page_faults);
    printf("Page Hit Ratio: %.2f%%\n", (1 - (float)page_faults / page_count) * 100);
}

void print_memory_state(Page pages[], int frame_count) {
    printf("Memory State: ");
    for (int i = 0; i < frame_count; i++) {
        printf("%d ", pages[i].page_number);
    }
    printf("\n");
}

代码分析】:这份C代码旨在模拟三种页面置换算法:FIFO(先进先出)、LRU(最近最少使用)和CLOCK(时钟)。首先,通过定义 `Page` 结构来表示页面,其中包括页面编号和用于LRU算法的 `referenced` 字段。

每个算法都有一个对应的函数:

1)FIFO算法 (`fifo` 函数):

   - 该函数接受四个参数:`Page pages[]` 用于表示内存中的页面,`page_sequence[]` 表示页面访问序列,`page_count` 表示页面访问序列的长度,`max_frames` 表示内存物理块的数量。

   - 模拟了每一步的内存状态,FIFO算法按照先进先出的原则置换页面。在给定的页面访问序列中,每当发生缺页,都会将最早进入内存的页面替换出去。最后,输出总的缺页次数和页面命中率。

2)LRU算法 (`lru` 函数):

   - 该函数同样接受四个参数:`Page pages[]` 表示内存中的页面,`page_sequence[]` 表示页面访问序列,`page_count` 表示页面访问序列的长度,`max_frames` 表示内存物理块的数量。

   - 模拟了每一步的内存状态,LRU算法根据最近最少使用的原则进行页面置换。每次发生缺页时,都会替换最久未被使用的页面。最后,输出总的缺页次数和页面命中率。

3)CLOCK算法 (`clock_algorithm` 函数):

   - 该函数同样接受四个参数:`Page pages[]` 表示内存中的页面,`page_sequence[]` 表示页面访问序列,`page_count` 表示页面访问序列的长度,`max_frames` 表示内存物理块的数量。

   -模拟了每一步的内存状态,CLOCK算法是一种时钟算法,根据页面的访问位进行页面置换。在发生缺页时,逐个检查页面的访问位,选择第一个访问位为0的页面进行替换。最后,输出总的缺页次数和页面命中率。

用户可以通过输入指定内存物理块数量和页面访问序列。整个程序的逻辑结构清晰,每个算法的实现都以独立的函数形式存在。

3. 编译test.c文件,运行a.out文件,输入内存物理块数量、访问页数和页面访问序列,输出三种页面调度算法的当前驻留在内存区的页面,缺页次数以及命中率。 

      

[结果分析]:三种页面调度算法的运行结果如下:

FIFO    最终内存页面:7 4 2 1  缺页次数:6次  命中率:40.00%

LRU     最终内存页面:4 7 6 1  缺页次数:7次  命中率:30.00%

CLOCK   最终内存页面:7 6 4 1  缺页次数:6次  命中率:40.00%

综合考虑,对于相同的输入序列,不同的页面置换算法展现出了各自独特的性能特征。每个算法都具有一些优势和局限,而其选择通常依赖于具体的应用场景和性能要求。

在这个具体的实例中,FIFO和CLOCK算法表现相似,这是因为它们都属于相对简单的置换策略。FIFO按照最早进入内存的页面进行替换,而CLOCK算法则通过维护一个类似时钟的访问位循环指针,以判断页面是否被访问。这些算法的简单性使其在某些场景下表现良好,但在某些复杂的访问模式下可能会产生不理想的结果。

相比之下,LRU算法则考虑了页面的使用频率,选择最近最少被使用的页面进行替换。然而,由于需要追踪每个页面的使用历史,LRU算法的实现相对复杂,可能会导致较高的计算成本。在这个例子中,LRU算法的性能稍逊,可能是由于其较为复杂的维护机制导致了额外的开销。

因此,在选择页面置换算法时,需要综合考虑应用场景的特点、系统资源的约束以及对性能的具体需求。不同的算法可能在不同的情境下表现更为优越。

(2)自行修改物理块分配个数以及访问序列,检验实现的页面置换算法的有效性和稳定性。

四  实验总结

1) FIFO算法分析

优点:简单易实现。FIFO算法是一种直观、简单的页面置换策略,容易理解和实现。

缺点:FIFO容易出现"Belady现象",即增加物理块数量反而可能导致缺页率增加。这使得FIFO在某些情况下不够灵活适应不同访问模式。

2)LRU算法分析

优点:LRU算法考虑了页面的使用频率,更加智能地选择置换页面,适应不同访问模式。

缺点:实现复杂。LRU的实现相对复杂,需要维护每个页面的使用历史,可能导致较高的计算开销。

3)CLOCK算法分析

优点:介于简单和智能之间。CLOCK算法介于FIFO和LRU之间,考虑了页面的使用情况,但相对实现较为简单。

缺点:对某些模式不敏感。CLOCK算法可能对某些访问模式不敏感,因为它主要关注页面的访问位而非实际的使用频率。

4)自定义测试

验证适应性:通过自定义测试,观察了算法对不同内存块数量和访问序列的适应性。

性能不一定提升:在某些情况下,增加物理块数量并不总是导致性能提升。这表明算法性能与具体场景和访问模式有关。

5)总体评价

选择算法需权衡:页面置换算法的选择需要权衡算法的复杂性和性能表现,具体应用场景会影响算法的优劣。

实验提供参考:实验结果为理解和选择适当的页面置换算法提供了实际的参考,对比了不同算法在具体场景下的表现。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值