本文最后更新于:2021年2月27日 上午
概览 :操作系统课程设计 —— 磁盘管理的实现,包含Java与C++,均采用面向对象的思想,包含代码。
任务要求 本次课程设计的任务是虚拟实现部分操作系统的典型算法,加深对操作系统运行机制的掌握和理解。任务具体要求:
在Winidows/Linux/iOS/Android平台下(平台任选一个),以面向对象思想 实现下面功能:
1、磁盘管理 建立一个4KB大小的文件模拟磁盘,按逻辑将其划分为1024块,每块大小4B。其中900块用于存放普通数据,124块用于存储兑换数据。存储管理需要支持:
(1)数据组织:对需要存放的文件数据加以组织管理,可以采用连续组织方式、显式连接(FAT)方式、单级索引组织方式、二级索引组织方式、混合索引方式(每组要求不同,具体见“课程设计分组”部分,下同)。
(2)空闲块管理:能够查询并返回当前剩余的空闲块,对空闲块管理可以采用位示图法、空闲盘块表法、空闲盘块连法、成组连接法。
(3)兑换区管理:能够写入、读出兑换区数据。
2、目录管理 为写入模拟磁盘的数据文件建立目录,目录可以是单级文件目录、双级文件目录、树形结构目录。在目录中选择某个文件可以将其数据读入模拟内存。目录中包含文件名、文件所有者、创建时间、文件结构、在磁盘中存放的地址等信息。目录管理需要支持:
(1)新建目录:在目录中新建空目录
(2)删除目录:删除空目录
(3)为文件建立目录项:一个文件被创建后,为该文件创建目录项,并将文件相关信息写入目录中。
(4)删除文件:删除目录中某个文件,删除其在磁盘中的数据,并删除目录项。如果被删除文件已经读入内存应该阻止删除,完成基本的文件保护。
3、内存管理 申请一块64字节的内存空间模拟内存,按逻辑划分为16块,每块4B。将目录中选中的文件读入内存,显示文件中信息。内存可以同时显示多个文件信息,每个文件固定分配4个内存块,如果4个内存块不能显示文件全部信息,采用页面置换策略,将已显示完的页换出内存,可以选择的置换策略有,全局置换、局部置换、FIFO、LRU。内存管理需要支持:
(1)分配内存块:为线程分配内存块,每个线程默认分配4块。
(2)回收内存:线程结束后回收其内存。
(3)空闲内存块管理:为进入内存的数据寻找空闲内存块。没有空闲内存时,应给出提示。
(4)块时间管理:提供数据块进入模拟内存的时间、访问时间的记录和查询功能,为页面置换算法提供支持,当某个内存块被选中时,更新其访问时间。
4、线程管理 本虚拟系统以线程为基本运行单位,线程本身采用编程语言提供的线程机制,不模拟。系统主要包括的线程有:
(1)数据生成线程:该线程负责生成外存数据,给定数据大小(按字节计算)、数据信息(英文字母)、存储目录、文件名后,该线程调用磁盘管理中空闲磁盘管理功能,申请所需大小的外存块,如果盘块不够给出提示。按照要求的数据组织方式,将数据存入磁盘块(按块分配磁盘),并调用目录管理功能为其在目录中建立目录项,更改空闲盘块信息。
(2)删除数据线程:该线程调用删除目录管理中文件删除功能删除数据(内存中文件不能删除)。并回收外存空间,更新空闲盘块信息。
(3)执行线程:选择目录中的文件,执行线程将文件数据从外存调入内存,为此,首先需要调用内存管理的空闲空间管理功能,为该进程申请4块空闲内存,如果没有足够内存则给出提示,然后根据目录中文件存储信息将文件数据从外存读入内存,此间如果4块内存不够存放文件信息,需要进行换页(选择的换页策略见分组要求),欢出的页面存放到磁盘兑换区。允许同时运行多个执行线程。文件数据在内存块的分布通过线程的页表(模拟)进行记录。
(4)线程互斥:对于64B的内存,线程需要互斥访问,避免产生死锁。不能访问内存的线程阻塞,等待被唤醒。
5、用户接口 对内存块、外存块、目录信息进行可视化显示,并能够动态刷新。文件调入内存过程、以及换页过程在块与块之间加入延时,以便观察。
对于实现以上功能,可以采用任何熟悉的编程语言,不做具体要求。
磁盘管理 由于我主要负责磁盘管理部分,所以只写了磁盘管理部分的代码。
Java代码 DiskBlock.java – 定义单独的一块磁盘块,即磁盘的最小结构。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package FileDiskDir;public class DiskBlock { private int blockNum; private String blockData; public DiskBlock () { } public DiskBlock (int blockNum, String blockData) { this .blockNum = blockNum; this .blockData = blockData; } public int getBlockNum () { return blockNum; } public void setBlockNum (int blockNum) { this .blockNum = blockNum; } public String getBlockData () { return blockData; } public void setBlockData (String blockData) { this .blockData = blockData; } }
DiskManage.java – 磁盘管理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 package FileDiskDir;import java.io.*;import java.util.HashMap;import java.util.Map;public class DiskManage { private Map<String, MixIndex> FileMixIndexMap = new HashMap<String, MixIndex>(); private DiskBlock[] MyDisk = new DiskBlock[1024 ]; private boolean [] MyDiskState = new boolean [1024 ]; private int freeDiskDataBlock; private String diskTxtData; private static DiskManage instance = new DiskManage(); public DiskManage () { freeDiskDataBlock = 900 ; serializeToDisktxt(); diskTxtData = readDataFromDiskTxt(); } public static DiskManage getInstance () { return instance; } private void serializeToDisktxt () { try { File f = new File("src/file/disk.txt" ); byte data[] = new byte [4096 ]; for (int i = 0 ;i<4096 ;i++){ data[i] = '#' ; } FileOutputStream fos = new FileOutputStream(f); fos.write(data); fos.close(); } catch (IOException e) { e.printStackTrace(); } } private String readDataFromDiskTxt () { String data = "" ; try { File f = new File("src/file/disk.txt" ); FileReader reader = new FileReader(f); BufferedReader br = new BufferedReader(reader); String lines; while ((lines = br.readLine() )!= null ){ data += lines; } br.close(); reader.close(); } catch (IOException e) { e.printStackTrace(); } return data; } private void writeDataToDiskTxt (String data) { try { File f = new File("src/file/disk.txt" ); f.createNewFile(); FileWriter writer = new FileWriter(f); BufferedWriter out = new BufferedWriter(writer); out.write(data); out.flush(); out.close(); writer.close(); } catch (IOException e) { e.printStackTrace(); } } public int getFreeDiskDataBlock () { return freeDiskDataBlock; } public void WriteDataToBlock (int blockNum,String data) { MyDisk[blockNum] = new DiskBlock(); MyDisk[blockNum].setBlockData(data); MyDiskState[blockNum] = true ; freeDiskDataBlock -= 1 ; diskTxtData = readDataFromDiskTxt(); StringBuffer sb = new StringBuffer(diskTxtData); for (int i=0 ;i<4 ;i++){ sb.setCharAt(blockNum*4 + i,data.charAt(i)); } writeDataToDiskTxt(sb.toString()); } public String ReadDataFromBlock (int blockNum) { return MyDisk[blockNum].getBlockData(); } public void DeleteDataInBlock (int blockNum) { MyDiskState[blockNum] = false ; freeDiskDataBlock += 1 ; } public MixIndex SaveFileDataToDisk (String fileName,int size,String data) { MixIndex fileMixIndexTable = new MixIndex(); MixIndex nowMixIndexTable = fileMixIndexTable; int index = 0 ; for (int i=0 ;i<900 ;i++) { if (MyDiskState[i] == false ){ if (index < size){ if (index / 9 == 0 && index % 9 == 0 ){ MixIndex newfileMixIndexTable = new MixIndex(); nowMixIndexTable.setIndirectIndex(newfileMixIndexTable); nowMixIndexTable = newfileMixIndexTable; } nowMixIndexTable.setDirectIndexByIndex(index%9 ,i); MyDiskState[i] = true ; WriteDataToBlock(i,data.substring(index*4 ,index*4 +4 )); index++; } else break ; } } FileMixIndexMap.put(fileName,fileMixIndexTable); return fileMixIndexTable; } public String ReadFileDataInDisk (String fileName,int size) { MixIndex fileMixIndexTable = FileMixIndexMap.get(fileName); String data = "" ; int times = size / 9 ; int lasttimes = size % 9 ; for (int i=0 ;i< times;i++){ for (int j = 0 ;j<9 ;j++){ data += ReadDataFromBlock(fileMixIndexTable.getDirectIndex(j)); } fileMixIndexTable = fileMixIndexTable.getIndirectIndex(); } for (int i = 0 ;i<lasttimes;i++){ data += ReadDataFromBlock(fileMixIndexTable.getDirectIndex(i)); } return data; } }
main.java – 测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import FileDiskDir.DiskManage;public class Main { public static void main (String[] args) { System.out.println("Hello World!" ); DiskManage disk = new DiskManage(); String data = "1234567812345678123456781234567812345678123456781234567812345678" ; disk.SaveFileDataToDisk("hello" ,16 ,data); System.out.println(disk.ReadFileDataInDisk("hello" ,16 )); } }
java代码大致思路
采用了普通的一维数组来表示位示图,用于记录磁盘块的状态。
MyDisk[1024] 来表示磁盘。存取操作都对这个数组进行。
Map<String, MixIndex> FileMixIndexMap 用来记录文件名与文件所存储的混合索引表的映射。
文件的读取操作都是之后加入的,用于同步磁盘的情况。
实际上的存储数据会有一定的bug(存储数据的第一部分,即索引表的第一部分读取时会出现问题)。
C++代码 common_data.h – 公共数据的结构体定义头文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #pragma once #include <iostream> #include <string> #include <map> #include <stdio.h> #include <stdlib.h> #include <conio.h> #include <ctime> #include <Windows.h> #include <iomanip> using namespace std ;struct DiskBlock { int BlockNum; string data; };struct DireIndexTable { int addr_1[10 ]; };struct IndireIndexTable { DireIndexTable addr_2[10 ]; };struct MixIndexTable { int addr_1[10 ]; DireIndexTable addr_2[10 ]; IndireIndexTable addr_3[10 ]; };
DiskManage.h – 磁盘管理类的定义头文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #pragma once #include "common_data.h" class DiskManage {public : DiskBlock MyDisk[1024 ]; map <string , MixIndexTable*> MyMixIndexTable; int BitMap[32 ][32 ]; int FreeDataBlockNum; int FreeSwapBlockNum; public : DiskManage(); ~DiskManage(); static DiskManage *getInstance () ; void WriteDataToBlock (int blockNum, string data) ; string ReadDataFromBlock (int blockNum) ; void DeleteDataInBlock (int blockNum) ; MixIndexTable* SaveFileDataToDisk (string fileName, int size, string data) ; int SaveMmToSwap (string data) ; string ReadFileDataFromDisk (string fileName, int size) ; void PrintMyDisk () ; void PrintBitMap () ; };
DiskManage.cpp – 磁盘管理类的实现文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 #include "DiskManage.h" DiskManage::DiskManage(){ for (int i = 0 ; i < 1024 ; i++) { MyDisk[i].BlockNum = i; MyDisk[i].data = "####" ; } for (int i = 0 ; i < 32 ; i++) { for (int j = 0 ; j < 32 ; j++) { BitMap[i][j] = 0 ; } } FreeDataBlockNum = 900 ; FreeSwapBlockNum = 124 ; } DiskManage::~DiskManage(){ }DiskManage * DiskManage::getInstance () { static DiskManage * instance; if (instance == nullptr ) { instance = new DiskManage(); } return instance; }void DiskManage::WriteDataToBlock (int blockNum, string data) { MyDisk[blockNum].data = data; int i = blockNum / 32 ; int j = blockNum % 32 ; BitMap[i][j] = 1 ; if (i >= 28 && j >= 4 ) { FreeSwapBlockNum -= 1 ; } else { FreeDataBlockNum -= 1 ; } }string DiskManage::ReadDataFromBlock (int blockNum) { return MyDisk[blockNum].data; }void DiskManage::DeleteDataInBlock (int blockNum) { MyDisk[blockNum].data = "####" ; int i = blockNum / 32 ; int j = blockNum % 32 ; BitMap[i][j] = 0 ; if (i >= 28 && j >= 4 ) { FreeSwapBlockNum += 1 ; } else { FreeDataBlockNum += 1 ; } }MixIndexTable * DiskManage::SaveFileDataToDisk (string fileName, int size, string data) { MixIndexTable *mit = new MixIndexTable; int index = 0 ; for (int blockNum = 0 ; blockNum < 900 ; blockNum++) { if (BitMap[blockNum / 32 ][blockNum % 32 ] == 0 ){ if (index < size) { if (index < 10 ) { mit->addr_1[index] = blockNum; } else if (index >= 10 && index < 110 ) { mit->addr_2[(index - 10 ) / 10 ].addr_1[(index - 10 ) % 10 ] = blockNum; } else { mit->addr_3[(index - 110 ) / 100 ].addr_2[((index - 110 ) % 100 ) / 10 ].addr_1[((index - 110 ) % 100 ) % 10 ]; } BitMap[blockNum / 32 ][blockNum % 32 ] == 1 ; WriteDataToBlock(blockNum, data.substr(index*4 ,4 )); index++; } else break ; } } MyMixIndexTable.insert(map <string ,MixIndexTable*>::value_type(fileName,mit)); return mit; }int DiskManage::SaveMmToSwap (string data) { int blockNum = 900 ; for (blockNum = 900 ; blockNum < 1024 ; blockNum++) { if (BitMap[blockNum / 32 ][blockNum % 32 ] == 0 ) { BitMap[blockNum / 32 ][blockNum % 32 ] == 1 ; WriteDataToBlock(blockNum, data); break ; } } return blockNum; }string DiskManage::ReadFileDataFromDisk (string fileName, int size) { MixIndexTable *mit = MyMixIndexTable.find(fileName)->second; string data = "" ; for (int i = 0 ; i < size; i++) { if (i < 10 ) { data.append(ReadDataFromBlock(mit->addr_1[i])); } else if (i >= 10 && i < 110 ) { data.append(ReadDataFromBlock(mit->addr_2[(i - 10 ) / 10 ].addr_1[(i - 10 ) % 10 ])); } else { data.append(ReadDataFromBlock(mit->addr_3[(i - 110 ) / 100 ].addr_2[((i - 110 ) % 100 ) / 10 ].addr_1[((i - 110 ) % 100 ) % 10 ])); } } return data; }void DiskManage::PrintMyDisk () { for (int i = 1 ; i <= 1024 ; i++) { cout << MyDisk[i-1 ].data << " " ; if (i % 16 == 0 ) cout << endl ; } }void DiskManage::PrintBitMap () { for (int i = 0 ; i < 32 ; i++) { for (int j = 1 ; j <= 32 ; j++) { cout << BitMap[i][j-1 ] << " " ; if (j % 16 == 0 ) cout << endl ; } } }
mian.cpp – 测试文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #include "DiskManage.h" #include <conio.h> int main () { cout << "打印磁盘" << endl ; DiskManage::getInstance()->PrintMyDisk(); cout << "打印位示图" << endl ; DiskManage::getInstance()->PrintBitMap(); cout << "\t\t----模拟创建文件----" << endl ; cout << "文件数据内容:1234xxzz5678ccvv9999oooo0x0xcccc1234xxzz5678ccvv9999oooo0x0xcccc" << endl ; string data = "1234xxzz5678ccvv9999oooo0x0xcccc1234xxzz5678ccvv9999oooo0x0xcccc" ; DiskManage::getInstance()->SaveFileDataToDisk("hello" ,16 ,data); cout << "\t\t----创建文件完成----" << endl ; cout << "打印磁盘" << endl ; DiskManage::getInstance()->PrintMyDisk(); cout << "打印位示图" << endl ; DiskManage::getInstance()->PrintBitMap(); cout << "\t\t----模拟数据写入交换区----" << endl ; cout << "文件数据内容:cccc" << endl ; data = "cccc" ; DiskManage::getInstance()->SaveMmToSwap(data); cout << "\t\t----数据写入交换区完成----" << endl ; cout << "打印磁盘" << endl ; DiskManage::getInstance()->PrintMyDisk(); cout << "打印位示图" << endl ; DiskManage::getInstance()->PrintBitMap(); _getch(); return 0 ; }
C++代码思路 第一遍是用Java写的,当时思路还不够清晰明确,混合索引定义也有问题,而且代码还有BUG,而且出于整个组的代码合并的考虑,改写成了C++。并且重新建立了结构体,以及混合索引表的结构。
混合索引表有三部分,第一部分是10个直接索引,直接指向存储数据的磁盘块号,这样能表示大小占10个块的数据;第二部分是10个间接索引,每个间接索引都指向了10个直接索引,实际上就是二维数组,这样能表示大小占100个块的数据;第三部分是10个二级索引,每个索引都指向了10个一级间接索引,而每个一级间接索引都能够指向10直接索引,实际上就是三维数组,这样就能够表示1000个块的数据。这样一个混合索引表最多能存储1110个块的数据,足够满足本次课设的要求。
位示图采用了 32*32的二维数组来表示,然后块号到位示图位置的映需要通过简单的计算映射。
建立了文件名与混合索引表的映射 – map<string, MixIndexTable*> MyMixIndexTable;这样就能根据文件名来找到文件对应的混合索引表了。
磁盘的构造函数里初始化了磁盘的数据,全部为“####”,且设置了数据区与交换区的块数。
分配外存采用了首次适应算法,即分配磁盘时永远从第一块磁盘开始进行搜索。
总结 本次课程设计做的比较心累,一方面时语言的不熟悉,一方面是对任务的要求不够清楚,比较模糊,最后是小组分工写代码,但要求代码合并到一起,没有统一的接口设置,代码写的非常混乱。
对比其他的大佬组,有大佬亲自定义了全部的框架与函数,只要求其他人实现函数就行,有的大佬写了图形界面,还有的大佬甚至用了安卓和数据库,我等菜鸡太卑微了~
代码下载 本次代码里有java实现的代码,以及小组最终去展示的C++代码。线程管理的那一部分完全没有做。
链接:https://pan.baidu.com/s/1HEWRvvY--VWiSOuBLwx3Fw 提取码:bm8g