guanjinquan пре 1 година
родитељ
комит
83d6d4d9a2
6 измењених фајлова са 411 додато и 1 уклоњено
  1. 4 1
      docs/.vuepress/config.js
  2. 0 0
      docs/Lab/Lab4.md
  3. 117 0
      docs/Lab/Lab4/CLOCK.md
  4. 105 0
      docs/Lab/Lab4/FIFO.md
  5. 114 0
      docs/Lab/Lab4/LRU.md
  6. 71 0
      docs/Lab/Lab4/README.md

+ 4 - 1
docs/.vuepress/config.js

@@ -12,7 +12,7 @@ module.exports = {
 				{ text: '实验1-熟悉类Linux系统', link: '/Lab/Lab1' },
 				{ text: '实验2-进程创建与进程间通信', link: '/Lab/Lab2' },
 				{ text: '实验3-进程调度算法', link: '/Lab/Lab3/' },
-				{ text: '实验4-存储管理算法', link: '/Lab/Lab4' },
+				{ text: '实验4-存储管理算法', link: '/Lab/Lab4/' }, // 这里 /Lab/Lab4/ 表明路径是目录
 				{ text: '实验5-文件管理系统', link: '/Lab/Lab5' },
 				{ text: '实验6-网络编程(暂定)', link: '/Lab/Lab6' }
 			]
@@ -62,6 +62,9 @@ module.exports = {
 				title: '存储管理',
 				children: [
 					['', '任务说明'],
+					['FIFO', 'FIFO页面置换算法'],
+					['LRU', 'LRU页面置换算法'],
+					['CLOCK', 'CLOCK页面置换算法'],
 				],
 			},
 		],

+ 0 - 0
docs/Lab/Lab4.md


+ 117 - 0
docs/Lab/Lab4/CLOCK.md

@@ -0,0 +1,117 @@
+# 时钟页面置换算法(CLOCK)
+
+
+
+## 置换策略与算法原理
+
+​	CLOCK页面置换算法的置换策略是淘汰最近未使用的页面,选择在内存中最长时间没有被访问的页面予以淘汰,而替换此时需要调用的页面。
+
+​	CLOCK页面置换算法的算法原理是利用一个循环队列来模拟内存中的页面,并为每个页面设置一个访问位。当某个页面首次装入内存中时,或者该页面被访问到时,将其访问位设置为1。当需要淘汰一个页面时,从当前指针所指的位置开始,检查每个页面的访问位。如果是0,则选择该页面换出,并将新页面放入该位置,并将其访问位设置为1;如果是1,则将其访问位置为0,并继续检查下一个位置。如果一轮扫描后所有页面的访问位都是1,则将所有访问位清零,并重新扫描。
+
+​	CLOCK页面置换算法的优点是实现起来比较简单,只需要维护一个循环队列和一个指针,不需要移动队列中的元素。CLOCK算法也是一种LRU置换算法的近似算法,能够较好地反映程序局部性原理。
+
+**扩展:**
+
+​	CLOCK页面置换算法的缺点是没有考虑到页面是否被修改过的情况,如果一个页面被修改过,则在换出时需要写回磁盘,这会增加I/O开销。
+
+​	因此,有一种改进型CLOCK算法,除了访问位外,还增加了一个修改位,用于记录每个页面是否被修改过,并根据不同的情况进行优先级排序。
+
+
+
+
+
+## 实验步骤
+
+​	接上文,在这里继续完成置换算法的实现。
+
+```cpp
+// 假设页面的结构体定义如下
+struct Page {
+  int read; // 读标志
+  int write; // 写标志
+  int time_arrive; // 到达时间
+  int access; // 访问位
+};
+
+// 定义一个页面置换算法CLOCK类
+class CLOCK {
+private:
+  int page_num; // 页面数量
+  int mem_size; // 内存大小
+  Page* pages; // 页面数组
+  Page** memory; // 内存数组,用于存放当前正在内存中的页面
+  int pointer; // 指针,用于指向当前检查的位置
+  int page_faults; // 缺页次数
+
+public:
+  // 构造函数,根据给定的页面数量,内存大小和页面数组初始化类的成员变量
+  CLOCK(int pn, int ms, Page* ps) {
+    page_num = pn;
+    mem_size = ms;
+    pages = ps;
+    memory = new Page*[mem_size]; // 动态分配内存数组的空间
+    for (int i = 0; i < mem_size; i++) { // 初始化内存数组,开始时都为空
+      memory[i] = nullptr;
+    }
+    pointer = 0; // 初始化指针为0,指向第一个位置
+    page_faults = 0;
+  }
+
+  // 析构函数,释放内存数组的空间
+  ~CLOCK() {
+    delete[] memory;
+  }
+
+  // 执行页面置换算法的函数,根据给定的页面访问序列进行操作,并输出结果
+  void run(Page* query[], int len) {
+    for (int i = 0; i < len; i++) { // 遍历所有页面访问序列
+      bool hit = false; // 是否命中标志
+      for (int j = 0; j < mem_size; j++) { // 遍历所有内存块
+        if (memory[j] == query[i]) { // 如果当前访问的页面已经在内存中
+          hit = true; // 设置命中标志为真
+          memory[j]->access = 1; // 更新该页面的访问位为1
+          break; // 跳出循环
+        }
+      }
+      if (!hit) { // 如果没有命中
+        page_faults++; // 缺页次数加一
+        bool replaced = false; // 是否替换标志
+        while (!replaced) { // 循环直到找到一个可以替换的位置为止
+          if (memory[pointer]->access == 0) { // 如果当前指针所指的位置的访问位为0,则可以替换该位置的页面
+            memory[pointer] = query[i]; // 将当前访问的页面放入该位置,并将其访问位设置为1
+            memory[pointer]->access = 1;
+            replaced = true; // 设置替换标志为真,跳出循环
+          } else { // 如果当前指针所指的位置的访问位为1,则将其访问位清零,并继续检查下一个位置
+            memory[pointer]->access = 0;
+            pointer = (pointer + 1) % mem_size; // 指针向前移动一位,如果到达末尾,则回到开头,形成循环队列
+          }
+        }
+      }
+    }
+
+    // 输出结果
+    cout << "CLOCK page replacement algorithm results:" << endl;
+    cout << "Page faults: " << page_faults << endl;
+    cout << "Memory contents: " << endl;
+    for (int i = 0; i < mem_size; i++) {
+      cout << "Memory block " << i << ": ";
+      if (memory[i] != nullptr) {
+        cout << "Page " << memory[i] - pages << endl;
+      } else {
+        cout << "Empty" << endl;
+      }
+    }
+  }
+};
+```
+
+
+
+
+
+## 实验样例
+
+
+
+
+

+ 105 - 0
docs/Lab/Lab4/FIFO.md

@@ -0,0 +1,105 @@
+# 先进先出算法
+
+
+
+## 置换策略与算法原理
+
+​	FIFO页面置换算法的置换策略是淘汰最先进入内存的页面,选择在内存中驻留最久的页面予以淘汰,而替换此时需要调用的页面。
+
+​	FIFO页面置换算法的算法原理是需把一个进程已调入内存的页面按先后顺序链接成一个队列,设置一个指针(替换指针),将指针指向最久调入的页面,若在此队列中已存在,则不进行替换;否则将替换指针所指向的页面出队,新调用的页面入队。
+
+​	FIFO页面置换算法的简单实现:可以通过维护一个队列结构去存储当前调入的页面;这样,当发生缺页中断时,需要进行置换的时候,淘汰队列头的页面,并把新调入的页面放入到队列尾部。
+
+​	FIFO页面置换算法的优点是其实现起来比较简单,可以不需要硬件的支持,因而不需要增加系统的成本。
+
+​	FIFO页面置换算法的缺点是没有考虑到缓存页面被使用的情况。如果一个页面被频繁访问, 我们应该将它保留在缓存中, 这样就能够提高程序的性能。但是使用FIFO算法, 很可能将一个被频繁访问的页面清除出缓存, 所以FIFO算法在实际的应用中是很少被使用到的。
+
+​	FIFO页面置换算法还可能出现Belady现象,即分配给进程的物理块数增加时,缺页率反而提高。这是因为FIFO算法将最早调入的页调出,而调出的页在不久可能会被重新使用出现反复调入调出,缺页率反而上升。
+
+
+
+## 实验步骤
+
+​	接上文,在这里继续完成置换算法的实现
+
+```cpp
+// 假设页面的结构体定义如下
+struct Page {
+  int read; // 读标志
+  int write; // 写标志
+  int time_arrive; // 到达时间
+  int time_access; // 访问时间
+};
+
+// 定义一个页面置换算法FIFO类
+class FIFO {
+private:
+  int page_num; // 页面数量
+  int mem_size; // 内存大小
+  Page* pages; // 页面数组
+  Page** memory; // 内存数组
+  int page_faults; // 缺页次数
+  int mem_index; // 内存指针
+
+public:
+  // 构造函数,根据给定的页面数量,内存大小和页面数组初始化类的成员变量
+  FIFO(int pn, int ms, Page* ps) {
+    page_num = pn;
+    mem_size = ms;
+    pages = ps;
+    memory = new Page*[mem_size]; // 动态分配内存数组的空间
+    for (int i = 0; i < mem_size; i++) { // 初始化内存数组,开始时都为空
+      memory[i] = nullptr;
+    }
+    page_faults = 0;
+    mem_index = 0;
+  }
+
+  // 析构函数,释放内存数组的空间
+  ~FIFO() {
+    delete[] memory;
+  }
+
+  // 执行页面置换算法的函数,根据给定的页面访问序列进行操作,并输出结果
+  void run(Page* query[], int len) {
+    for (int i = 0; i < len; i++) { // 遍历所有页面访问序列
+      bool hit = false; // 是否命中标志
+      for (int j = 0; j < mem_size; j++) { // 遍历所有内存块
+        if (memory[j] == query[i]) { // 如果当前访问的页面已经在内存中
+          hit = true; // 设置命中标志为真
+          break; // 跳出循环
+        }
+      }
+      if (!hit) { // 如果没有命中
+        page_faults++; // 缺页次数加一
+        memory[mem_index] = query[i]; // 将当前访问的页面放入内存中,替换最先进入的页面
+        mem_index = (mem_index + 1) % mem_size; // 更新内存指针,循环移动
+      }
+    }
+
+    // 输出结果
+    cout << "FIFO page replacement algorithm results:" << endl;
+    cout << "Page faults: " << page_faults << endl;
+    cout << "Memory contents: " << endl;
+    for (int i = 0; i < mem_size; i++) {
+      cout << "Memory block " << i << ": ";
+      if (memory[i] != nullptr) {
+        cout << "Page " << memory[i] - pages << endl;
+      } else {
+        cout << "Empty" << endl;
+      }
+    }
+  }
+};
+```
+
+
+
+
+
+## 实验样例
+
+
+
+
+

+ 114 - 0
docs/Lab/Lab4/LRU.md

@@ -0,0 +1,114 @@
+# 最近最久未使用算法(LRU)
+
+
+
+## 置换策略与算法原理
+
+​	LRU页面置换算法的置换策略是淘汰最近最久未使用的页面,选择在内存中最长时间没有被访问的页面予以淘汰,而替换此时需要调用的页面。
+
+​	LRU页面置换算法的算法原理是根据页面调入内存后的使用情况来做决策的。无法预估页面将来的使用情况,利用过去的页面使用情况来进行置换的。
+
+​	LRU页面置换算法的一种实现方法是使用一个数组`pages[]`存放当前正在内存中的页面,每一次访问页面就只需要更新`time_access`变量,当需要页面置换的时候,选出`pages[]`中`time_access`最小的页面,然后把他从数组替换成需要调入的页面。
+
+​	※ LRU页面置换算法的另一种比较合理和高效实现方法是利用一个哈希表和一个双向链表来模拟缓存系统。哈希表用于记录每个页面的地址,双向链表用于记录每个页面被访问的先后顺序。每当进程访问页面时,便将该页面从链表中删除,并将它插入到链表头部。这样,链表头部始终是最新被访问页面的地址,而链表尾部则是最近最久没有被使用的页面的地址。
+
+​	LRU页面置换算法的优点是能够较好地反映程序局部性原理,即最近被访问过的页面很可能在不久后再次被访问。因此,LRU算法能够减少缺页次数,提高程序性能。
+
+​	LRU页面置换算法的缺点是实现起来比较复杂,需要维护额外的数据结构来记录每个页面的使用情况,并且需要频繁地更新这些数据结构。因此,LRU算法需要较多的时间和空间开销。
+
+
+
+## 实验步骤
+
+​	接上文,在这里继续完成置换算法的实现
+
+```cpp
+// 假设页面的结构体定义如下
+struct Page {
+  int read; // 读标志
+  int write; // 写标志
+  int time_arrive; // 到达时间
+  int time_access; // 访问时间
+};
+
+// 定义一个页面置换算法LRU类
+class LRU {
+private:
+  int page_num; // 页面数量
+  int mem_size; // 内存大小
+  Page* pages; // 页面数组
+  Page** memory; // 内存数组,用于存放当前正在内存中的页面
+  int page_faults; // 缺页次数
+
+public:
+  // 构造函数,根据给定的页面数量,内存大小和页面数组初始化类的成员变量
+  LRU(int pn, int ms, Page* ps) {
+    page_num = pn;
+    mem_size = ms;
+    pages = ps;
+    memory = new Page*[mem_size]; // 动态分配内存数组的空间
+    for (int i = 0; i < mem_size; i++) { // 初始化内存数组,开始时都为空
+      memory[i] = nullptr;
+    }
+    page_faults = 0;
+  }
+
+  // 析构函数,释放内存数组的空间
+  ~LRU() {
+    delete[] memory;
+  }
+
+  // 执行页面置换算法的函数,根据给定的页面访问序列进行操作,并输出结果
+  void run(Page* query[], int len) {
+    for (int i = 0; i < len; i++) { // 遍历所有页面访问序列
+      bool hit = false; // 是否命中标志
+      for (int j = 0; j < mem_size; j++) { // 遍历所有内存块
+        if (memory[j] == query[i]) { // 如果当前访问的页面已经在内存中
+          hit = true; // 设置命中标志为真
+          memory[j]->time_access = i + 1; // 更新该页面的访问时间为当前序号加一(避免为零)
+          break; // 跳出循环
+        }
+      }
+      if (!hit) { // 如果没有命中
+        page_faults++; // 缺页次数加一
+        int min_time = INT_MAX; // 最小访问时间,初始为最大整数值
+        int min_index = -1; // 最小访问时间对应的内存块索引,初始为-1
+        for (int k = 0; k < mem_size; k++) { // 遍历所有内存块,寻找最小访问时间的页面
+          if (memory[k]->time_access < min_time) { // 如果当前内存块中的页面访问时间小于最小访问时间
+            min_time = memory[k]->time_access; // 更新最小访问时间为当前访问时间
+            min_index = k; // 更新最小访问时间对应的内存块索引为当前索引
+          }
+        }
+        memory[min_index] = query[i]; // 将当前访问的页面放入内存中,替换最近最久未使用的页面
+        memory[min_index]->time_access = i + 1; // 更新该页面的访问时间为当前序号加一(避免为零)
+      }
+    }
+
+    // 输出结果
+    cout << "LRU page replacement algorithm results:" << endl;
+    cout << "Page faults: " << page_faults << endl;
+    cout << "Memory contents: " << endl;
+    for (int i = 0; i < mem_size; i++) {
+      cout << "Memory block " << i << ": ";
+      if (memory[i] != nullptr) {
+        cout << "Page " << memory[i] - pages << endl;
+      } else {
+        cout << "Empty" << endl;
+      }
+    }
+  }
+};
+```
+
+
+
+
+
+
+
+## 实验样例
+
+
+
+
+

+ 71 - 0
docs/Lab/Lab4/README.md

@@ -0,0 +1,71 @@
+# 实验四 - 页面置换算法
+
+## 前言
+
+(1)为什么要引入页面置换?
+
+(2)页面置换的性能指标是什么
+
+(3)页面置换算法的实现
+
+
+
+## 一、实验目的 
+
+​	请求页式管理是现代通用操作系统中最常用的一种虚拟存储管理技术,本实验的目的是通过模拟请求页式存储管理中的页面替换算法,了解虚拟存储技术的特点,认识操作系统对内存的管理。
+
+
+
+## 二、实验原理
+
+(1)回顾请求页式管理:
+
+​	进程运行时,若其访问的页面不在内存中,而需将其调入,但是内存已无空闲内存时,就需要从内存中调出一页程序或数据,送入磁盘的对换区(swap分区)。**选择调出页面的算法就叫页面置换算法。** 
+
+
+
+(2)页面算法好坏的评估指标:
+
+​	好的页面置换算法应有较低的页面更换频率,即应将以后不会再访问或以后较长时间不会访问的页面先调出。总的来说就是:具有较小的缺页中断次数。
+
+- 缺页中断:CPU在检索页表的时候发现进程要访问的页面不在物理内存中,就会发出一个Page Fault。
+
+
+
+
+
+## 三、实验步骤
+
+### 1. 数据类型
+
+(1)你需要一个class类或者struct来表示已经存在物理内存中的页框Page的信息,至少需要包含:
+
+- page_number表示页号
+- read、write来表示页面是否被读写,以便在页面替换的时候判断是否需要把页面写回磁盘。
+
+​	此外,根据你实验的算法的不同,你可以为该Page类添加更多的成员变量,比如:
+
+|   变量名    |           内涵           |
+| :---------: | :----------------------: |
+| time_arrive | 页面调入物理内存的时间点 |
+| time_access |  最近一次被访问的时间点  |
+
+
+
+(2)你需要设置一个变量MemPageNumber来表示内存中实页的数量。
+
+
+
+### 2. 算法模型
+
+​	选取以下算法为主题,实现一个页面调度算法,并输出**页面命中率**或者**缺页次数**。
+
+- FIFO
+- LRU
+- CLOCK
+
+
+
+
+
+## 课外资料