操作系统课程实验3-可变分区存储管理

操作系统课程实验3-可变分区存储管理

一、实验介绍

1.1 实验目的
  1. 加深对可变分区存储管理的理解;
  2. 提高用C语言编制大型系统程序的能力,特别是掌握C语言编程的难点:指针和指针作为函数参数;
  3. 掌握用指针实现链表和在链表上的基本操作。
1.2 实验内容

参照教材P137-P140的内容,编写一个C程序,用循环首次适应算法、最佳适应算法和最坏适应算法,模拟可变分区存储管理,实现对内存区的分配和回收管理。

1.3 实验要求
  1. 每次分配和释放后显示空闲分区表。
  2. 空闲分区表可采用结构数组的形式(最低要求)或双向链表的形式。
1.4 参考测试数据

操作系统在低地址占用100KB的空间,用户区主存从100KB处开始占用512KB。初始时,用户区全部为空闲,分配时截取空闲分区的低地址部分作为已分配区。执行以下申请、释放操作序列后:请求300KB,请求100KB,释放300KB,请求150KB,请求90KB,释放100KB。

二、实现代码

#include<stdio.h>  
#include<stdlib.h> 
#include<iostream> using namespace std; #define ALLOCATED 1 						// 定义已分配状态为1
#define FREE 0 								// 定义空闲状态为0
#define SEQUENCELENGTH 6 					// 定义操作序列长度为6typedef struct CHAIN { 						// 定义链表结构体 Tableint status; 							// 记录块的状态:ALLOCATED 或 FREEint pid; 								// 记录分配给的进程IDint size; 								// 记录块的大小int addr; 								// 记录块的起始地址struct CHAIN *Next_Block; 				// 指向下一块的指针struct CHAIN *Pre_Block; 				// 指向前一块的指针
} Table; Table *Head; 								// 头指针,指向链表的头部
int OCPSequence[SEQUENCELENGTH][3] = { 		// 操作序列数组,包含分配和回收操作{1, 1, 300}, {1, 2, 100}, {2, 1, 300}, {1, 3, 150}, {1, 4, 90}, {2, 2, 100}
};void start(); 								// 声明启动函数void FirstFit(); 							// 声明首次适应算法函数
void BestFit(); 							// 声明最佳适应算法函数
void WorstFit(); 							// 声明最坏适应算法函数
void CirFirstFit(); 						// 声明循环首次适应算法函数bool recycle(int pid); 						// 声明回收函数
void linkprint(void); 						// 声明链表打印函数
void CommomDeal(int alg); 					// 声明通用处理函数Table* FindFF(int size); 					// 声明首次适应算法查找函数
Table* FindBF(int size); 					// 声明最佳适应算法查找函数
Table* FindWF(int size); 					// 声明最坏适应算法查找函数
Table* FindCFF(int size); 					// 声明循环首次适应算法查找函数void HeadReset(void); 						// 声明头节点重置函数int main(void) {Head = (Table*)malloc(sizeof(Table)); 	// 分配内存给头节点Head->Next_Block = Head->Pre_Block = NULL; // 初始化头节点的前后指针HeadReset(); 							// 重置头节点start(); 								// 启动模拟return 0; 
}void start() {cout << "we are gonna do a simulation about memory allocation" << endl; cout << "next,there are some algorithm is waiting for you,you need to choose one of them in every circle" << endl; cout << "1.首次适应算法\t2.最佳适应算法\t3.最坏适应算法\t4.循环首次适应算法" << endl << endl << endl << endl; int IDofAlgorithm; 						// 声明算法ID变量for (int i = 0; i < 4; i++) { 			// 循环4次,模拟4个算法cout << "输入算法ID:"; 			cin >> IDofAlgorithm; 				// 输入算法IDwhile (IDofAlgorithm < 1 || IDofAlgorithm > 4) { // 检查输入是否合法cout << "无效的算法ID,请重新输入:"; cin >> IDofAlgorithm; }switch (IDofAlgorithm) { 			// 根据算法ID选择对应算法case 1:printf("--------------------------------------------------------------------------------------\n                                     首次适应算法                                     \n--------------------------------------------------------------------------------------\n"); // 打印首次适应算法标题FirstFit(); 				// 调用首次适应算法函数break;case 2:printf("--------------------------------------------------------------------------------------\n                                     最佳适应算法                                     \n--------------------------------------------------------------------------------------\n"); // 打印最佳适应算法标题BestFit(); 					// 调用最佳适应算法函数break;case 3:printf("--------------------------------------------------------------------------------------\n                                     最坏适应算法                                     \n--------------------------------------------------------------------------------------\n"); // 打印最坏适应算法标题WorstFit(); 				// 调用最坏适应算法函数break;case 4:printf("--------------------------------------------------------------------------------------\n                                   循环首次适应算法                                   \n--------------------------------------------------------------------------------------\n"); // 打印循环首次适应算法标题CirFirstFit(); 				// 调用循环首次适应算法函数break;}HeadReset(); 						// 重置头节点,准备下一个算法测试}
}void FirstFit() {CommomDeal(1); // 调用通用处理函数,参数为1(首次适应算法)
}
void BestFit() {CommomDeal(2); // 调用通用处理函数,参数为2(最佳适应算法)
}
void WorstFit() {CommomDeal(3); // 调用通用处理函数,参数为3(最坏适应算法)
}Table* FindFF(int size) {Table * p = Head; 					// 初始化指针p为头节点while (p != NULL ) { 				// 遍历链表if (p->size >= size && p->status == FREE) // 找到第一个合适的空闲块return p; 					// 返回该块指针p = p->Next_Block; 				// 移动到下一个块}return NULL; 						// 如果没有合适的块,返回NULL
}Table* FindBF(int size) {Table * p = Head; 					// 初始化指针p为头节点Table * best = NULL; 				// 初始化最优块指针为NULLwhile (p != NULL ) { 				// 遍历链表if (p->status == FREE && p->size >= size && (best == NULL || p->size < best->size)) // 找到更小的合适空闲块best = p; 					// 更新最优块指针p = p->Next_Block; 				// 移动到下一个块}return best; 						// 返回最优块指针
}Table* FindWF(int size) {Table * p = Head; 					// 初始化指针p为头节点Table * Worst = NULL; 				// 初始化最差块指针为NULLwhile (p != NULL ) { 				// 遍历链表if (p->status == FREE && p->size >= size && (Worst == NULL || p->size > Worst->size)) // 找到更大的合适空闲块Worst = p; 					// 更新最差块指针p = p->Next_Block; 				// 移动到下一个块}return Worst; 						// 返回最差块指针
}Table* FindCFF(Table * p, int size) {Table * Flag = p; 					// 保存初始块指针if (p == NULL) 						// 如果初始块为空p = Head; 						// 从头开始while (p != NULL) { 				// 遍历链表if (p->status == FREE && p->size >= size) // 找到合适的空闲块return p; 					// 返回该块指针if (!p->Next_Block) 			// 如果到达链表末尾p = Head; 					// 回到头节点elsep = p->Next_Block; 			// 移动到下一个块if (p == Flag) 					// 如果回到初始块return NULL; 				// 返回NULL表示没有找到合适块}
}void CommomDeal(int alg) {Table* (*Tar)(int); 				// 定义函数指针switch (alg) { 						// 根据算法选择对应查找函数case 1:Tar = FindFF; // 指向首次适应算法查找函数break;case 2:Tar = FindBF; // 指向最佳适应算法查找函数break;case 3:Tar = FindWF; // 指向最坏适应算法查找函数break;}for (int i = 0; i < SEQUENCELENGTH; i++) { 			// 遍历操作序列if (OCPSequence[i][0] == 1) { 					// 分配操作int ID, size;ID = OCPSequence[i][1]; 					// 获取进程IDsize = OCPSequence[i][2]; 					// 获取请求大小Table  *pReturn;printf("进程%d请求%dKB\n", ID, size); 		// 打印请求信息printf("--------------------------------------------------------------------------------------\n");if ((pReturn = Tar(size)) != NULL) { 		// 调用查找函数,找到合适块if (pReturn->size == size) { 			// 如果块大小正好pReturn->pid = ID; 					// 设置块的进程IDpReturn->status = ALLOCATED; 		// 设置块的状态为已分配} else { 								// 如果块大小大于请求大小Table * NewFreeNode = (Table*)malloc(sizeof(Table)); // 创建新空闲块NewFreeNode->pid = -1; 				// 初始化新块NewFreeNode->addr = pReturn->addr + size; // 设置新块地址NewFreeNode->size = pReturn->size - size; // 设置新块大小NewFreeNode->status = FREE; 		// 设置新块状态为空闲pReturn->pid = ID; 					// 设置当前块的进程IDpReturn->size = size; 				// 设置当前块的大小pReturn->status = ALLOCATED; 		// 设置当前块的状态为已分配NewFreeNode->Pre_Block = pReturn; 	// 设置新块的前指针NewFreeNode->Next_Block = pReturn->Next_Block; // 设置新块的后指针pReturn->Next_Block = NewFreeNode; 	// 设置当前块的后指针为新块}} else { 									// 如果没有合适的块cout << "内存不足,分配失败" << endl; 	// 打印失败信息}} else { 										// 回收操作int pid;pid = OCPSequence[i][1]; 					// 获取要回收的进程IDprintf("请求回收进程%d,即将释放内存空间%dKB\n", pid, OCPSequence[i][2]); // 打印回收请求信息if (!recycle(pid)) 							// 调用回收函数cout << "进程不存在,回收失败" << endl; // 打印回收失败信息elsecout << "回收成功" << endl; 			// 打印回收成功信息}linkprint(); 									// 打印当前链表状态printf("--------------------------------------------------------------------------------------\n");}cout << endl << endl << endl;
}void CirFirstFit() {Table *Current = NULL; 								// 初始化当前指针为空for (int i = 0; i < SEQUENCELENGTH; i++) { 			// 遍历操作序列if (OCPSequence[i][0] == 1) { 					// 分配操作int ID, size;ID = OCPSequence[i][1]; 					// 获取进程IDsize = OCPSequence[i][2]; 					// 获取请求大小printf("进程%d请求%dKB\n", ID, size); 		// 打印请求信息printf("--------------------------------------------------------------------------------------\n");if ((Current = FindCFF(Current ? Current->Next_Block : Head, size)) != NULL) { // 调用循环首次适应查找函数if (Current->size == size) { 			// 如果块大小正好Current->pid = ID; 					// 设置块的进程IDCurrent->status = ALLOCATED; 		// 设置块的状态为已分配} else { 								// 如果块大小大于请求大小Table * NewFreeNode = (Table*)malloc(sizeof(Table)); // 创建新空闲块NewFreeNode->pid = -1; 				// 初始化新块NewFreeNode->addr = Current->addr + size; // 设置新块地址NewFreeNode->size = Current->size - size; // 设置新块大小NewFreeNode->status = FREE; 		// 设置新块状态为空闲Current->pid = ID;					// 设置当前块的进程IDCurrent->size = size; 				// 设置当前块的大小Current->status = ALLOCATED; 		// 设置当前块的状态为已分配NewFreeNode->Pre_Block = Current; 	// 设置新块的前指针NewFreeNode->Next_Block = Current->Next_Block; // 设置新块的后指针Current->Next_Block = NewFreeNode; 	// 设置当前块的后指针为新块}} else { 									// 如果没有合适的块cout << "内存不足,分配失败" << endl; 	// 打印失败信息}} else { 										// 回收操作int pid;pid = OCPSequence[i][1]; 					// 获取要回收的进程IDprintf("请求回收进程%d,即将释放内存空间%dKB\n", pid, OCPSequence[i][2]); // 打印回收请求信息if (!recycle(pid)) 							// 调用回收函数cout << "进程不存在,回收失败" << endl; // 打印回收失败信息elsecout << "回收成功" << endl; 			// 打印回收成功信息}linkprint(); 									// 打印当前链表状态printf("--------------------------------------------------------------------------------------\n");}cout << endl << endl << endl;
}bool recycle(int pid) {Table *p = Head; 									// 初始化指针p为头节点while (p) { 										// 遍历链表if (p->pid == pid) { 							// 找到匹配的进程IDp->status = FREE; 							// 设置块状态为空闲// 合并相邻的空闲块if (p->Next_Block && p->Next_Block->status == FREE) { // 如果下一块也是空闲Table *next = p->Next_Block; 			// 保存下一块指针p->size += next->size; 					// 合并大小p->Next_Block = next->Next_Block; 		// 更新当前块的后指针if (next->Next_Block) {next->Next_Block->Pre_Block = p; 	// 更新下一块的前指针}free(next); 							// 释放合并的块}if (p->Pre_Block && p->Pre_Block->status == FREE) { // 如果前一块也是空闲Table *prev = p->Pre_Block; 			// 保存前一块指针prev->size += p->size; 					// 合并大小prev->Next_Block = p->Next_Block; 		// 更新前一块的后指针if (p->Next_Block) {p->Next_Block->Pre_Block = prev; 	// 更新当前块的前指针}free(p); 								// 释放合并的块p = prev; 								// 更新指针p为前一块}return true; 								// 返回回收成功}p = p->Next_Block; 								// 移动到下一个块}return false; 										// 如果没有找到匹配的进程ID,返回回收失败
}void linkprint(void) {Table *p = Head; 									while (p) { 									if (p->status == 0) 							cout << "" << p->addr << "~" << p->addr + p->size << ":" << p->status <<  ""; // 打印空闲块信息else 											cout << "" << p->addr << "~" << p->addr + p->size << ":" << p->status << " by " << p->pid << ""; // 打印已分配块信息if (p->Next_Block) 								cout << "-->"; 								p = p->Next_Block; 								}cout << endl; 									
}void HeadReset(void) {Table* p = Head->Next_Block; 			// 初始化指针p为头节点的下一块Table *temp; 							// 声明临时指针while (p) { 							// 遍历链表temp = p->Next_Block; 				// 保存下一块指针free(p); 							// 释放当前块p = temp; 							// 更新指针p为下一块}Head->addr = 100; 						// 重置头节点地址Head->Next_Block = NULL; 				// 重置头节点后指针Head->Pre_Block = NULL; 				// 重置头节点前指针Head->pid = -1; 						// 重置头节点进程IDHead->size = 500; 						// 重置头节点大小Head->status = FREE; 					// 重置头节点状态为空闲
}

三、心灵的救赎

  1. 人间没有永恒的夜晚,世界没有永恒的冬天。
  2. 当生活把无边的严寒铺盖在你身上时,一定会给你一根火柴。
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/14135.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

React Native 之 图片使用(六)

静态图片资源 React Native项目中&#xff0c;图片文件的查找会和 JS 模块的查找方式一样。它提供了一个统一的方式来管理 iOS 和 Android 应用中的图片。要往 App 中添加一个静态图片&#xff0c;只需把图片文件放在代码文件夹中某处&#xff0c;然后像下面这样去引用它&…

如何设计足够可靠的分布式缓存体系,以满足大中型移动互联网系统的需要?no.31

传统 CAP 的突破 随着分布式系统的不断演进&#xff0c;会不断遇到各种问题&#xff0c;特别是当前&#xff0c;在大中型互联网系统的演进中&#xff0c;私有云、公有云并行发展且相互融合&#xff0c;互联网系统的部署早已突破单个区域&#xff0c;系统拓扑走向全国乃至全球的…

新书推荐:6.2 else if语句

本节必须掌握的知识点&#xff1a; 示例代码二十 代码分析 汇编解析 ■if语句表达形式3 if(表达式1) statement1 else if(表达式2) statement2 else if(表达式3) statement3 …… else statementN 解析&#xff1a; 如果表达式1非0&#xff0c;则执行statement1&#…

记录github小程序短视频系统的搭建过程

GitHub - lkmc2/AwesomeVideoWxApp: 《倾心短视频》微信小程序 这个项目按readme中的来可以部署成功&#xff0c;但是会发现图片、视频全是空的&#xff0c;如下图&#xff1a; 修改源代码&#xff0c;更换图片上传与保存地址 大概涉及到这些代码块&#xff0c;进行更改即可。…

什么样的数据摆渡设备,可以满足不同网间数据的安全传输需求?

数据摆渡设备是用来在不同的网络环境间安全地传输数据的硬件或软件解决方案。它们通常用于确保在具有不同安全级别的网络&#xff08;如内网和外网&#xff09;之间进行数据交换时的安全性和合规性。以下是一些常见的数据摆渡设备和方法&#xff1a; 移动介质拷贝&#xff1a;使…

生产制造边角料核算说明及ODOO演示

今天群里有伙伴提到边角料的处理问题&#xff0c;我们梳理了一下&#xff0c;在生产过程中&#xff0c;如果产生了边角料&#xff0c;核算产成品的投料成本时需要考虑边角料的价值&#xff0c;以确保成本核算的准确性。以下是注意的几点&#xff1a; 一、边角料的入账价值 在生…

OSPF路由聚合

原理概述 与RIP不同&#xff0c;OSPF不支持自动路由聚合&#xff0c;仅支持手动路由聚合。OSPF的路由聚合有两种机制&#xff1a;区域间路由聚合和外部路由聚合。区域间路由聚合必须配置在ABR路由器上&#xff0c;指的是ABR在把与自己直接相连区域&#xff08;Area&#xff09…

K8s 二进制部署---下篇(多master节点 负载均衡 高可用)

一 master02 节点部署 master01192.168.11.5kube-apiserver&#xff0c;kube-controller-manager&#xff0c;kube-scheduler&#xff0c;etcdmaster02192.168.11.12kube-apiserver&#xff0c;kube-controller-manager&#xff0c;kube-scheduler&#xff0c;etcdnode01192.1…

RHEL7及之后系统 系统服务脚本(Rocky 9.4)

目录 源码安装 准备工作 步骤1: 下载软件 步骤2: 安装apr 步骤3: 安装apr-util 步骤4: 安装Apache HTTP Server 总结步骤 后续步骤 源码安装 准备环境&#xff1a;首先&#xff0c;确保你的系统中安装了必要的编译工具和依赖库。对于C/C程序&#xff0c;这通常包括编译器&#…

网络流量监控与流量回溯分析:全面指南

目录 什么是网络流量监控&#xff1f; 网络流量监控的主要功能 什么是流量回溯分析&#xff1f; 流量回溯分析的优势 网络流量监控与流量回溯分析的结合 如何实现有效的网络流量监控与回溯分析&#xff1f; 网络流量监控与流量回溯分析的应用场景 企业网络 数据中心 …

PEI转染试剂残留检测

转染试剂是瞬时转染生产病毒载体的主要原材料之一。聚乙烯亚胺&#xff08;Polyethylenimine, PEI&#xff09;以其适用性广泛、成本低廉、操作简便、细胞毒性低、可以在多种哺乳动物细胞中达到较高的转染效率&#xff0c;适用于无血清悬浮转染环境&#xff0c;包装容量不受限制…

【Python快速上手(二十七)】- Python3 SMTP发送邮件

目录 Python快速上手&#xff08;二十七&#xff09;- Python3 SMTP发送邮件Python3 SMTP发送邮件1. SMTP 基本概念2. 使用 smtplib 发送简单邮件2.1 设置 SMTP 服务器2.2 登录到 SMTP 服务器2.3 发送邮件2.4 关闭连接 3. 构建复杂邮件3.1 构建带有 HTML 内容的邮件3.2 发送带有…

Zookeeper 面试题(六)

1. 简述Zookeeper中的脑裂问题 &#xff1f; 在分布式系统中&#xff0c;脑裂&#xff08;Split-brain&#xff09;问题是指由于网络分区或其他故障导致系统分裂成两个或多个独立的、相互之间无法通信的部分&#xff0c;每个部分都认为自己是整个系统的唯一活跃部分。在ZooKee…

一阶数字高通滤波器

本文的主要内容包含一阶高通滤波器公式的推导和数字算法的实现以及编程和仿真 1 计算公式推导 1.1.2 算法实现及仿真 利用python实现的代码如下&#xff1a; import numpy as np # from scipy.signal import butter, lfilter, freqz import matplotlib.pyplot as plt #2pifW…

从原理上解决 uniapp (含第三方插件)打包 iOS APP 失败的问题

最近一段时间&#xff0c;我的团队基于uniapp开发的平台型APP因平台资金合规的要求&#xff0c;需要对接中金支付&#xff0c;uniapp的插件市场有一个别人做好的中金支付插件&#xff0c;但前端开发同事在引用这个 插件时&#xff0c;出现了 iOS APP 打包不成功的情况&#xff…

Pantera 合伙人简谈 Morpho:更高效、适应性更强的 DeFi 解决方案

原文标题&#xff1a;《Pioneering Peer-to-Peer Lending in the DeFi Revolution》撰文&#xff1a;Pantera Capital 合伙人 Paul Veradittakit编译&#xff1a;Chris&#xff0c;Techub News 文章来源&#xff1a;香港Web3媒体Techub News Morpho 正在超越 Compound 等传统…

Redis主从、哨兵、cluster集群的部署和细节

目录 1. 主从模式 为什么需要主从&#xff1f; 搭建主从架构 2. Sentinel(哨兵)模式 为什么需要哨兵模式&#xff1f; 搭建哨兵集群 哨兵集群 Go语言编程redis哨兵模式 有了哨兵&#xff0c;客户端连接谁&#xff1f; test1&#xff1a;redis节点主从切换 test2&am…

webgl入门-矩阵变换

矩阵变换 前言 变换有三种状态&#xff1a;平移、旋转、缩放。 当我们变换一个图形时&#xff0c;实际上就是在移动这个图形的所有顶点。 课堂目标 掌握图形变换的三种方式。可以对图像进行复合变换。 知识点 平移旋转缩放 第一章 平移 对图形的平移就是对图形所有顶点…

如何快速从手动测试转向自动化测试

寻求具有无缝持续集成和持续交付 (CI/CD) 的高效 DevOps 管道比以往任何时候都更加重要。想象一下这样一个场景&#xff1a;您的软件组织显著减少了人工工作量、降低了成本&#xff0c;并更加自信地发布了软件更新。换句话说&#xff0c;通过将 Web UI 和 API 测试结合在一起&a…

【小白课程】如何在openKylin上个性化定制开关机动画

开关机动画是Linux系统的重要组成部分&#xff0c;其主要功能是在Linux内核启动的早期遮盖内核打印日志&#xff0c;并在内核刷新屏幕分辨率时保证屏幕显示的流畅性。 其中&#xff0c;openKylin操作系统使用plymouth组件作为开关机动画显示程序。Linux系统在启动时&#xff0…