华农操作系统课设 华南农业大学操作系统课程设计 题目六 模拟操作系统实现

题目六 模拟操作系统实现

取走点赞,代码已上传到 gitee https://gitee.com/wanglingfu/scau-os.git

一、 需求分析
模拟一个采用多道程序设计方法的单用户操作系统,该操作系统包括进程管理、存 储管理、设备管理、文件管理和用户接口四部分,要求:
1、 文件管理和用户接口
实现的主要是单用户的磁盘文件管理部分,包括文件的逻辑结构、物理结构、目录、磁盘分配回收、文件的保护和用户接口的实现。

  1. 支持多级目录结构,支持文件的绝对读路径。
    在图形化界面实现目录树结构,以及文件的路径显示查找等。

  2. 文件的逻辑结构采用流式结构,物理结构采用链接结构中的显式链接方式。系统中有两种文件,一种是存放任意字符的普通文本文件,一种是可执行文件。

  3. 采用文件分配表 FAT。
    把磁盘中每一块的指针部分提出来,组织在一起,形成文件分配表(FAT)。建立一个FAT数组作为文件分配表。

  4. 实现的命令包括建立目录、列目录、删除空目录、建立文件、删除文件、显示。
    实现图形界面的建立目录、列目录、删除空目录、建立文件、删除文件、显示操作,并对后台进行改变。

  5. 文件内容、打开文件、读文件、写文件、关闭文件。实现图形界面的显示文件内容、打开文件、读文件、写文件、关闭文件并对后台进行改变。
    命令行界面执行这些命令,也可以采用“右击快捷菜单选择”方式执行命令。
    2、 存储管理
    储管理部分主要实现内存空间的分配和回收、存储保护。用链表模拟内存空间分配表。存储管理采用动态分区存储管理方式,采用首次适配、下次适配、最佳适配均可。

  6. 模拟系统中,主存部分分为两部分,一部分是系统区,只存放进程控制块和内存分配表,一部分是用户区,存放可执行文件。系统区包括PCB区域(最多容纳10个PCB)、内存空间分配表;用户区用数组模拟,大小为512字节。

  7. 屏幕显示要求包括:内存使用情况示意图,以不同的颜色表示哪些区域未分配或已分配(已分配给哪个进程)。
    3、 设备管理
    设备管理主要包括设备的分配和回收。

  8. 模拟系统中有A、B、C三种独占型设备,A设备2个,B设备3个,C设备3个。(同一种设备的不同实体被认为是相同的)

  9. 设备的申请是由于前述可执行文件中的!??指令引起,有空闲设备时分配设备,然后进程阻塞,同时设备使用倒计时至0后释放设备(不考虑设备具体的I/O操作)并唤醒进程继续运行;无空闲设备时阻塞进程,直至其它进程释放设备时才分配设备并使用,设备使用完后唤醒进程。设备使用倒计时期间,本进程阻塞,需要调度另外一个进程去占用CPU执行;假设设备使用完后立即释放该设备,后续指令需再次使用该设备时重新分配。

  10. 不考虑死锁。

  11. 屏幕显示要求包括:每个设备是否被使用,哪个进程在使用该设备,哪些进程在等待使用该设备。
    4、 进程管理主要包括进程调度,进程的创建和撤销、进程的阻塞和唤醒,中断作用的实现。
    1)硬件工作的模拟
    (a)中央处理器的模拟用函数CPU()(该函数没有参数)模拟单个中央处理器。该函数主要负责解释“可执行文件”中的指令。(为简单计,用户命令接口部分实现的命令不必用CPU()解释执行)
    (b)主要寄存器的模拟用全局变量或数组模拟重要寄存器,
    (c)中断的模拟I、中断的发现应该是硬件的工作,但在这里,用在函数CPU()中检测PSW的方式来模拟。
    (d)时钟的模拟系统时钟和相对时钟用全局变量模拟。系统时钟用来记录开机以后的单位时间,相对时钟用来存放进程可运行的时间片
    2)进程控制块PCB进程控制块内容包括进程标识符、主要寄存器、进程状态、阻塞原因等。
    3)进程调度随机选择前面创建的10个可执行文件之一,创建进程PCB,分配内存,然后逐条执行其中的指令;然后经过随机时间后,再选择一个可执行文件,创建进程……,如此往复,模拟操作系统中进程随机到达的过程。采用时间片轮转调度算法,时间片长度为6。
    4)进程控制建立4个函数模拟进程创建、撤销、阻塞和唤醒四个原语
    5)屏幕显示要求包括:显示系统时钟;显示正在运行进程的进程ID、运行的指令、中间结果(x的当前值)、相对时钟(时间片剩余值);显示就绪队列中进程ID;33显示阻塞队列中进程ID。
    二、概要设计
    2.1 文件操作

  12. 模拟磁盘需要先设计一个类Disk存储磁盘里的信息。因为磁盘有256块,每块有64字节,所以用一个二维的byte数组存储磁盘里的信息。磁盘的信息存储在一个文本文件中,当要操作磁盘时便从文本文件读取内容存放到Disk类的数组中,操作完毕便写入文本文件中。
    属性为private byte[][] disk;

  13. 文件和目录管理需要文件分配表FAT,所以需要一个类FAT来管理文件分配表的改动。因为每个盘块要用一字节表示,而每个盘块有64字节,所以需要disk中的前4个盘块作为文件分配表的内容,所以FAT直接获取Disk中二维数组的引用,操作时直接在数组前四行里操作。
    属性为private byte[][] diskBuffer;

  14. 需要FileUtil类,用以对磁盘进行各种操作。因为磁盘中存储的都是字节型数据,所以从磁盘中读取内容都是将磁盘中的二进制内容转换为String类型,而写入时则将String型的内容与名字转换为字节型存储到磁盘中。文件或目录的路径是按如下的方法化为二进制的,如路径/ab/cde化为二进制时是转换为数组二进制数组path[][],其中path[0]是 “ab”的Ascii码[97,98],path[1]是”cde”的Ascii码[99,100,101]。属性如下。
    private byte[][] diskBuffer; //存储磁盘里的内容
    private final int ROOT_DIR = 4; //根目录在磁盘中的盘块
    private final byte EXE_PROPERTY = 5; //可执行文件属性
    private final byte TXT_PROPERTY = 1; //文本文件属性
    private final byte DIR_PROPERTY = 3; //目录属性
    private FAT fileAllocationTable; //文件分配表类
    private Disk disk; //磁盘类,用以从文件中读取内容
    private byte[] emptyBlock; //空盘块
    private byte[] emptyItem; //空目录项

2.2文件界面

ADT 磁盘块显示按钮MyButton{
数据对象:int number
数据关系:由JButton继承而来,number是MyButton特有的的成员变量。
基本操作:MyButton(String s,Icon icon,int num):根据字符串、图标构造,带有额外的number属性。
} ADT磁盘块显示按钮MyButton

ADT 命令行文本框CmdTextArea{
数据对象:
String work;
String path;
boolean permission;
StringBuffer textBuffer;
int currentDot;
boolean isAllowedInputArea;
int currentKeyCode;
boolean isConsume;
数据关系:
permission、currentDot、isAllowedInputArea、currentKeyCode、isConsume作为控制命令行显示及操作的限制符。(如permission为true表示具有管理员操作权限)
work存储当前用户的输入所代表的命令,path处理并保存用户命令所代表的路径、textBuffer存储用户输入进缓冲区的字符。它们都用来存储命令行文本框中用户的操作信息。
基本操作:(只解释重要的)
void emptyWork() 置work为空
String getWork() 返回work
public int getCurrentKeyCode() 获取键盘输入
public String getPathString() 返回路径
void keyTyped(KeyEvent e) 根据用户的输入获取命令的操作类型(增删改查)和操作的目录项路径
void keyPressed(KeyEvent e) 键盘按下控制
void keyReleased(KeyEvent e) 键盘释放控制
boolean checkConsume(KeyEvent e) 忽略用户的一些键盘操作,限制用户的输入操作。
void append(String message) 把字符串读入缓冲区
void caretUpdate(CaretEvent e) 控制光标要在最后才允许用户输入
void changePermission() 改变用户权限
String getPath(String withBlankPath) 根据用户输入获取目录项路径
} ADT命令行文本框CmdTextArea

ADT文件系统界面框ViewInitialization{
数据对象:
int currentKeyCode; //获取命令行输入
static JFrame jf; //swing主窗口
JPanel contentPane; //窗口主面板
JPanel catalog; //文件目录面板
JPanel commandLine; //命令行面板
CmdTextArea cmdTextArea; //命令行文本框
JPanel view; //磁盘块视图面板
JPanel right; //主面板右间距
JPanel bottom; //主面板下间距
JPanel left; //主面板左间距
JPopupMenu listPopupMenu; //目录的菜单项
JPopupMenu filePopupMenu; //文件的菜单项
JPopupMenu rootPopupMune; //根目录菜单项
JTree tree; //目录树结构
DefaultMutableTreeNode selectionNode; //当前选取的目录项结点
TreePath movePath; //当前结点的路径
FileUtil fileUtil; //文件内部磁盘操作接口
JButton buttons[]; //磁盘块按钮
DefaultMutableTreeNode rootNode; //根目录
数据关系:以jf为最上层的框架,contentPane为窗口主面板,在contentPane中加入目录、命令行和磁盘块视图面板,其他数据是三大面板中再往下的小部件。
基本操作:
initial() 变量初始化
void surface(int width, int height, JFrame jf) 界面初始化
JComponent view() 返回磁盘块视图控件
void updateImage() 根据用户选中的目录项更新磁盘块视图
JComponent command() 返回命令行界面视图控件
JComponent catalog() 返回目录控件
String getPathString(DefaultMutableTreeNode node) 以字符串形式获取结点的路径
DefaultMutableTreeNode getRootNode(String root) 构建目录树并返回根目录
void menuItemProcessing() 右键目录项弹出的菜单项设置
boolean isFile(DefaultMutableTreeNode node) 判定结点是否为文件
JMenuItem createMenuItem(String text, String action) 创建对应的菜单项
void showDiskTable(Frame owner, Component parentComponent) 显示文件的属性、即磁盘块占用情况
void showDirectoryCreation() 创建目录时候的弹出框及相关操作
void showFileCreation(Frame owner, Component parentComponent) 创建文件时候的弹出框及相关操作
void setView() 设置各个面板的布局,用分隔布局分隔开
void showTxtFile(Frame owner, Component parentComponent) 显示打开文本的界面
JPanel getBorderPane(JComponent p, int northBorder, int southBorder, int westBorder, int eastBorder) 设置组件的间距
boolean deleteItem(String path) 删除文件或目录
int addDirectory(String newNodeString)添加目录
int addFile(String newNodeString,String content) 添加文件
boolean setSelectionNode(String path,int type) 设置当前选中的结点
} ADT文件系统界面框ViewInitialization
2.3存储管理和设备管理
2.3.1 存储管理
存储管理部分主要实现内存空间的分配和回收存储保护。用链表模拟内存空间分配表。 存储管理采用动态分区存储管理方式 ,采用首次适配、下次适配、最佳适配均可。
根据实验要求定义以下抽象数据类型:
(1)定义空间分配表,包括每个申请到内存的进程的uid、内存块的起始地址、 内存块的大小、内存块的空闲状态。
数据对象:
public class Hole {
private String uid; // 进程uid
private int head; //内存块的起始地址
private int size; //内存块的大小
private boolean isFree; //内存块的空闲状态

(2)定义内存分配表
数据对象:
private String id;
private Hole hole;
(3)定义内存,其中内存512字节
数据对象:
int id1;
private int size; //内存块大小
private int lastfind; //上次寻址结束的下标
private LinkedList pcbs;//内存分配表
private LinkedList holes;//空间分配表
private static final int MIN_SIZE = 0;
操作:
public boolean BestFit(int size,String id) //最佳适应算法
public void getMemory(int size,int location,String id,Hole hole) //申请内存
public void releaseMemory(String id) //内存回收
2.3.2 设备管理
设备管理主要包括设备的分配和回收。(可设一张“设备分配表”和设备等待队列)
模拟系统中有A、B、C三种独占型设备,A设备2个,B设备3个,C设备3个。(同 一种设备的不同实体被认为是相同的)
设备的申请是由于前述可执行文件中的!??指令引起,有空闲设备时分配设备,然后进程阻塞,同时设备使用倒计时至0后释放设备(不考虑设备具体的I/O操作)并唤醒进程继续运行;无空闲设备时阻塞进程,直至其它进程释放设备时才分配设备并使用,设备使用完后唤醒进程。
根据实验要求定义如下抽象数据类型:
(1)定义设备分配表
数据对象
private String A1; //A、B、C分别代表三种设备,数字则代表设备号
private String A2;
private String B1;
private String B2;
private String B3;
private String C1;
private String C2;
private String C3;
(2)定义设备等待队列
数据对象:
private String uid; //等待设备进程的uid
private int size; //进程所需要分配的大小
private int time; //进程所需要占用设备的时间
(3)定义设备类对象
数据对象:
//A、B、C三种设备的等待队列
private LinkedList blockA ;
private LinkedList blockB ;
private LinkedList blockC ;
//设备分配表
private DeviceTable deviceTable;
操作:
public int getDevice(String Uid, int time, int size ) //申请设备
public int[] removeDevice(String Uid) //移除设备
public int gerFirstNode() //获取等待队列第一个节点

2.4 进程管理
进 程 管 理 主 要 包 括 进 程 调 度 ,进 程 的 创 建 和 撤 销 、进 程 的 阻 塞 和 唤 醒 , 中断作用的实现。包括中央处理器的模拟、主要寄存器的模拟、中断的模拟、时钟的模拟、进程控制块 PCB、进程调度和进程控制。

2.4.1 进程运行
数据对象:
private Integer AX; 数据寄存器
private int[] PSW = {0,0,0}; 中断标志寄存器
private String IR; 指令寄存器
private Integer PC = 0; 程序计数器
private byte[] file; 运行中的进程文件
private int flag ; 是否运行完
private int finalAX; 运行结束之后显示进程的AX
private String uuid; 运行中进程uuid
public int SystemTime=0; 系统时间
public int TimeSlice=6; 时间片
public int[] DeviceTime = {-1,-1,-1,-1,-1,-1,-1,-1}; 设备时间
private ProcessScheduling processScheduling; 进程调度
public static ReentrantLock lock = new ReentrantLock(); 进程锁
public static Condition condition1 = lock.newCondition();
public static Condition condition2 = lock.newCondition();

操作:
public void recovery(PCB pcb) 恢复主要寄存器
public PCB preservation(PCB pcb) 保存寄存器到PCB中
public void cpu() 进程运行
private void psw1() 进程终止
private void psw2() 进程时间片结束
private void psw3() 设备中断
private void psw3Util(String uuid) 设备中断util
public void time() 时间运行函数
2.4.2 进程控制块 PCB
数据对象:
private String uuid; 进程标识符
private String reason; 阻塞原因, 哪个设备
private Integer time; 阻塞时间
private byte[] file; 文件
private Integer AX; 数据
private Integer PC; 程序计数器
private ProcessScheduling processScheduling; 进程调度
2.4.3 进程调度
数据对象:
private Integer ProcessNum; 现有进程数,只允许最多10个
private Queue readyPCB; 就绪PCB队列
private ArrayList blockPCB; 阻塞PCB队列
private PCB runPCB运行中的进程
private Memory memory; 主存
private Device device; 设备
操作:
public boolean create(byte[] file) 创建进程
public void destroy()销毁进程
public int block(String reason) 阻塞进程
public int[] awake(PCB pcb) 唤醒进程
public void util(int select) 工具类 替换现有进程
2.4.4 进程工具类
public static byte[] getByteFile(String file) String文件转为byte文件
public static String getStringFile(byte[] file) byte文件转为String文件
public static String byteToString(byte IR) byte命令转为String命令
public static byte compile(String IR) byte命令转为String命令
进程命令编码规则:

  • x++:00000000
  • x—:00100000
  • !A(1-7):01000xxx
  • !B(1-7):01001xxx
  • !C(1-7):01010xxx
  • end:01100000
  • x=?:1xxxxxxx
  • !A(8-11):000010xx
  • !B(8-11):000110xx
  • !C(8-11):001010xx
    三、详细设计
    3.1文件操作
  1. Disk类
    主要实现在文本文件中存储和读取模拟磁盘内容的功能。

Public Disk() throws Exception{
readDisk();
}

//从磁盘读文件
public void readDisk() throws Exception{
创建文件输入流;
创建对象输入流;
从文件中读二维byte数组到disk数组中;
关闭输入流;
}

//从磁盘写文件
public void writeDisk() throws Exception{
创建文件输出流;
创建对象输出流;
将disk数组写入文件中;
关闭输出流;
}

//获取磁盘内容
public void getDisk() throws Exception{
返回二维字节数组disk;
}
//将字符串转换为字节数组
public byte[] stringToBytes(String string){
byte[] bytes = string.getBytes(Charset.forName(“UTF-8”));
return bytes;
}

  1. FAT类
    实现文件分配表的更新功能。文件分配表中0为未分配,1为盘块已分配且没有指向后继盘块,大于1的数说明盘块指向的后继盘块。
    public FAT(Disk disk) throws Exception {
    diskBuffer = disk.getDisk();
    }
    //获取输入盘块连接的下一盘块
    protected int getNextBlock(int 盘块序号){
    int 下一盘块序号 = 0;
    int i = 盘块序号 / 64;
    int j = 盘块序号 % 64;
    下一盘块序号 = diskBuffer[i][j];
    return 下一盘块序号;
    }

//从头查找整个文件分配表中的空盘块并返回分配到的盘块序号,返回-1为分配失败
public int assignBlock(){
int blockIndex = -1;
boolean 分配成功 = false;
for(int i=0; i<4 && !分配成功; i++){
for(int j=0; j<64; j++){
if(diskBuffer[i][j] == 0){
blockIndex = i*64 + j;
diskBuffer[i][j] = 1;
分配成功 = true;
break;
}
}
}
返回blockIndex;
}

//给盘块blockIndex分配下一盘块并连接两个盘块,返回-1表示分配失败
public int assignNextBlock(int blockIndex){
int i = blockIndex / 64;
int j = blockIndex % 64;
int 分配的盘块序号;
分配的盘块序号 = assignBlock();
//修改文件分配表中blockIndex指向的下一盘块
diskBuffer[i][j] = (byte)分配的盘块序号;
return assignedIndex;
}

//将文件分配表中的盘块序号指向的位置置为0,0代表为分配
public void freeBlock(int 盘块序号){
int i = blockIndex / 64;
int j = blockIndex % 64;
diskBuffer[i][j] = 0;
return ;
}
//将文件分配表中序号blockIndex盘块的盘块和后续连接的所以盘块改为未分配
public void freeBlocks(int blockIndex){
int nextBlock = blockIndex;
int currentBlock = nextBlock;
while(currentBlock != 1){
nextBlock = getNextBlock(currentBlock);
freeBlock(currentBlock);
currentBlock = nextBlock;
}
}

3) FileUtil类实现文件与目录的读写等操作
//在序号为blockIndex盘块中查找是否存在名字为name,属性为property的目录项,存在则返回该目录项所指向的盘块
public int getBlock(byte[] name,byte property, int blockIndex){
int block = -1;
byte[] dirName;
//每个目录项8字节,所以1个盘块中有8个目录项
for (int j=0; j<8; j++) {
//与blockIndex盘块中的目录项比较是否为所找目录项
if (属性匹配&&名字匹配) {
block = 目录项指向的起始盘块序号;
break;
}
}
return block;
}

/根据目录的路径path找到该目录所指向的盘块,path数组是将String型的路径一层一层转换为二进制路径,如路径/ab/cde,二维path数组就是{[97,98],[99,100,101]},因为是磁盘中存储的都是二进制,所以是通过二进制匹配/
public int findDirectory(byte[][] path){
//从根目录第五盘块开始按照路径一层一层向下配对查找
int blockIndex = ROOT_DIR;
int nextBlockIndex = blockIndex;
for(int i=0; i<path.length && 当前匹配的目录项存在; i++) {
do{
blockIndex = getBlock(path[i], DIR_PROPERTY, nextBlockIndex);
nextBlockIndex = 获取当前盘块指向的下一盘块;
}
while(当前盘块有后续盘块&&未找到匹配的目录项);
nextBlockIndex = blockIndex;
}
return nextBlockIndex;
}

//给文件或目录分配磁盘空盘块及创建目录项,name为文件名,property为文件属性,dirBlock是所在文件夹的盘块序号,isExecutable是否为可执行文件
public void assignDirectorySpace(byte[] name, byte property, byte isExecutable, int dirBlock){
int blockIndex = 0;
int assignedBlock;
byte[] freeSpace = new byte[3];
blockIndex = 将要创建目录项的父目录盘块序号;
if(父目录盘块没有空间创建新目录项){
blockIndex = 给父目录分配一个新盘盘块序号;
}
assignedBlock = 在文件分配表中分配空闲的盘块;
byte[] byteFile = new byte[8];
byteFile = 创建目录项内容;
将byteFile内容复制到磁盘中;
}

//创建文件夹。返回1代表路径错误,2代表磁盘或根目录已满,3代表存在同名目录
public int makeDirectory(String path) throws Exception{
int dirBlock = 将要创建文件夹的父目录项所指向盘块;
if(不存在父目录){
return 1;
}
If(有空余空间&&不存在同名目录){
给目录分配一个空盘块;
在父目录盘块创建目录项;
}
else{
if(目录或盘块已满){
return 2;
}
else{
return 3;
}
}
写入磁盘文件;
return 0;
}

//创建文件,path为文件路径,content为文件内容

public int createFile(String path, String content) throws Exception{
byte property = TXT_PROPERTY;
if(path.contains(".e")){
property = EXE_PROPERTY;
}
int dirBlock = 父目录的起始盘块;
if(dirBlock == -1){
return 1;
}
if(磁盘或根目录已满){
return 2;
}
if(路径下有同名同属性文件){
return 3;
}
if(property == TXT_PROPERTY){
在父目录下创建文本文件目录项;
给文件分配一个空盘块;
byteContent = 将String类型的文件内容转换为二进制内容,UTF-8编码;
}
else{
在父目录下创建可执行文件目录项;
给文件分配一个空盘块;
byteContent = 将String类型的执行内容经过编译后转换为二进制可执行内容;
}
//将文件内容写入盘块
writeFile(bytePath[bytePath.length-1], byteContent, property, dirBlock);
return 0;
}

//写入文件内容,name为文件名字,content为文件的二进制内容,property为文件属性,dirBlock为要写入的起始盘块序号,返回true为写入成功,false为写入失败。
public boolean writeFile(byte[] name, byte[] content, byte property, int dirBlock) throws Exception{
byte[] bytes = content;
int length = bytes.length;
int blockNum = length / 64 + ((length % 64)==0 ? 0:1);
if(文件分配表中的空盘块不足){
return false;
}
在文件分配表中分配空盘块;
//将文件内容写入盘块中
writeContent(bytes, headBlock);
将磁盘写入文件;
return true;
}

//写入文件内容,content为文件内容,headBlock为文件写入的起始盘块
public void writeContent(byte[] content, int headBlock){
int 文件占用空盘块数量 = content.length / 64;
int 文件指向最后一个空盘块将占用的字节数 = content.length % 64;
int 文件当前写入的磁盘块 = headBlock;
for(int i = 0; i < 文件占用空盘块数量; i++){
将文件内容按顺序复制到盘块中
}
if(最后一个空盘块将占用字节 > 0){
将文件余下内容写入指向的最后盘块中
}
}

//删除文件,path为文件目录,true为删除成功
public boolean deleteFile(String path) throws Exception{
byte property = TXT_PROPERTY;
if(如果文件名中包含”.exe”){
property = EXE_PROPERTY;
}
int dirBlock = 文件目录项所指向的起始盘块;
if(dirBlock == -1){
return false;
}
将该文件在文件分配表中分配的盘块清0;
删除文件在父目录中的目录项;
if(文件的目录项所在盘块在删除该目录项后变为空){
将父目录的空盘块的前一盘块指向的下一盘块改为空盘块的下一盘块;
}
将磁盘写入文件;
return true;
}

//删除空文件夹,path为文件夹目录,成功删除返回true
public boolean removeDirectory(String path) throws Exception{
找到文件夹目录项所指向盘块;
if(目录项指向盘块 == -1){
return false;
}
if(目录项指向盘块不是空盘块){
return false;
}
在文件分配表中清0目录项指向盘块;
删除父目录下的目录项;
将磁盘写入文件;
return true;
}
//删除文件夹及文件夹下的所有文件和文件夹,path为文件夹路径,返回0为删除成功,1为路径错误
public int deleteAll(String path) throws Exception{
找到文件夹目录项指向盘块;
if(无法文件夹指向盘块){
return 1;
}
ArrayList list =文件夹下的所有文件和文件夹;
for(int i=0; i< list.size(); i++){
if(list.get(i)是文件){
删除文件;
}
else{
deleteAll(list.get(i));
}
}
删除空文件夹path;
return 0;
}

//返回路径为path的文件夹下的所有目录项的名字
public ArrayList getDirectorys(String path){
ArrayList directorys = new ArrayList();
String name;
int blockIndex = 文件夹的起始盘块;
do{
for(int i=0; i<8; i++){
if(第i个目录项非空){
name = “/” + 目录项名字加后缀名;
directorys.add(name);
}
}
blockIndex = 指向的文件夹的下一盘块;
}
while(blockIndex 指向的下一盘块非空);
return directorys;
}

//获取文件内容并将内容转换为字符串返回,path为文件路径
public String getFileContent(String path){
String contentString = null;
byte property = TXT_PROPERTY;
if(path.contains(".e")){
property = EXE_PROPERTY;
}
int blockIndex = 文件的起始盘块;
int length = 文件内容长度;
byte[] content = new byte[length];
int blockNum = 文件占用盘块数;
int itemNum = 最后一个盘块中的字节数;
int i;
for(i=0; i < blockNum; i++){
将blockIndex指向盘块中的内容添加到content中;
blockIndex 指向该文件的下一盘块;
}
if(itemNum > 0){
该文件最后一个盘块中的内容添加到content中;
}
if(property == EXE_PROPERTY){
contentString = 将content转换为String型;
}
else{
contentString = 将content反编译成String型;
}
return contentString;
}

//复制文件,srcPath为原文件路径,destPath为复制的目的路径
public int copyFile(String srcPath, String destPath) throws Exception{
String content = 获取文件内容;
return createFile(destPath,content);
}

3.1.2文件操作函数调用图

3.2文件界面
3.2.1CmdTextArea中的操作(省略了过于简单的操作)
//根据用户的输入获取命令的操作类型(增删改查)和操作的目录项路径
public void keyTyped(KeyEvent e){
if (当前位置不允许用户输入) {
消除用户输入;
return;
}
if (键盘输入是回车) {
获取用户输入字符串并读入缓冲区;
if(用户的权限是管理员权限){
if(输入指令是"exit"){
work置为空;
改变用户权限;
输出相关提示;
}
else if(用户输入是"show instructions"或"si"){
显示命令行操作集;
}
else if(输入指令是"create"){
获取操作路径;
如果操作路径超过3个字符且不含有".txt"和".exe",则置path为空;
if(格式错误){
work=null;
输出相关提示;
}
else{
操作设置为"create";
this.path=path;
}
}
else if(输入指令是"delete"){
获取操作路径;
如果格式错误输出相关提示,否则操作设置为"delete";
}
else if(输入指令是"open"){
获取操作路径;
如果格式错误输出相关提示,否则操作设置为"open";
}
else if(输入指令是"copy"){
获取操作路径;
如果格式错误输出相关提示,否则操作设置为"copy";
}
else if(输入指令是"mkdir"){
获取操作路径;
如果格式错误输出相关提示,否则操作设置为"mkdir";
}
else if(输入指令是"rmdir"){
获取操作路径;
如果格式错误输出相关提示,否则操作设置为"rmdir";
}
else{
输入指令错误的提示;
}
}
else{
如果输入是"cmd",则设置授予用户管理员权限,否则输入指令错误的提示。
}
}
}

//忽略用户的一些键盘操作,限制用户的输入操作。
public boolean checkConsume(KeyEvent e){
if (该位置不允许输入字符) {
消除用户输入;
return true;
}
if (用户输入的是空格、回车、上、左按键且当前光标处字符数与缓冲区相同) {
消除用户输入;
return true;
}
return false;
}

//根据用户输入获取目录项路径
public static String getPath(String withBlankPath){ //格式正确返回路径,错误返回null
用最后一个空格分隔字符串;
把路径String类型转换为char数组;
if (数组长度为0){
return null;
}
if (第一个字符不是’/’){
return null;
}
for (从第2个字符遍历数组) {
if(字符是字母或’.’){
continue;
}else if(字符是’/’){
if(下一个字符是字母){
下标增1;
continue;
}else{
return null;
}
}else{
return null;
}
}
return path;
}

3.2.2ViewInitialization中的操作(设置组件的细节不显示)
//变量初始化
private void initial() throws Exception(){
初始化各数据成员
}

//界面初始化
private void surface(int width, int height, JFrame jf) throws Exception {
initial();
把contentPane设置为jf的主面板;
setView();
设置左右和底部的边距为10;
jf.setSize(width, height);
jf.setLocation(0, 0);
}

//返回磁盘块视图控件
private JComponent view() { //磁盘盘块视图
为256个buttons类型变量分配空间;
updateImage();
for(int i=0;i<256;i++) view.add(buttons[i]);
设置组件的边距为15;
设置布局为浮动布局,方式为左对齐;
在描述面板添加标签以及图标;
把描述面板设置为view面板的上部面板;
创建一个滚动面板,加入view为主要面板,并设置滚动量为50。
return scrollPane;
}

//根据用户选中的目录项更新磁盘块视图
private void updateImage() {
int buttonsAttribute[] = fileUtil.getFAT();
for(遍历buttonsAttribute[])
if(选中目录项是根目录){
buttonsAttribute[4] = 2;
}
else if (选中其他目录项) {
ArrayList nowOccupied = fileUtil.getFileBlock(getPathString(selectionNode));
for (遍历nowOccupied) {
buttonsAttribute[i] = 2;
}
}
for (定义变量i从0到255) {
if (buttonsAttribute[i]为0) {
设置其图标为未占用盘块;
} else if (buttonsAttribute为2) { //2表示当前目录项占用的
设置其图标为被选中盘块);
} else {
设置其图标为被占用盘块);
}
}
}

//根据用户输入操作并返回命令行界面视图控件
private JComponent command() throws Exception {
设置为BorderLayout布局;
cmdTextArea.addKeyListener(cmdTextArea);
cmdTextArea.addKeyListener(new KeyAdapter() {
public void keyTyped(KeyEvent e) {
super.keyTyped(e);
获取键盘输入;
if (输入的是回车){
if(操作是"create"){
try {
获取操作路径;
if(路径不包含".txt"和".exe")
path=path+".txt";
if(输入路径有误){
提示相关错误;
}
else{
int p;
if(path包含".exe"){
p=addFile(path,“end”);
}
else
p = addFile(path,“default”);
switch §{
输出对应信息;
}
}
} catch (Exception exception) {
exception.printStackTrace();
}
}
else if(操作是"delete"){
try {
获取操作路径;
if(输入路径有误){
提示错误;
}
else{
boolean t = deleteItem(path);
if (成功删除) {
显示删除成功;
} else {
显示相关错误;
}
}
} catch (Exception exception) {
exception.printStackTrace();
}
}
else if(操作是"open"){
获取操作路径;
if(输入路径有误){
提示错误;
}
else{
打开文件;
}
}
else if(操作是"copy"){
try {
获取操作路径;
分开存根路径和目的路径;
if(操作路径有误){
输出错误提示;
}
else{
获取根路径文件的内容;
添加相应文件;
switch §{
输出对应提示;
}
}
} catch (Exception exception) {
exception.printStackTrace();
}
}
else if(操作是"mkdir"){
try {
获取操作路径;
if(路径有误){
提示错误;
}
else{
添加对应目录;
根据添加函数返回值输出对应提示;
}
} catch (Exception exception) {
exception.printStackTrace();
}
}
else if(操作是"rmdir"){
try {
获取操作路径;
if(路径有误){
提示错误;
}
else{
if(目录飞空){
提示错误;
}
else{
根据路径删除该空目录;
根据函数返回值输出对应信息;
}
}
} catch (Exception exception) {
exception.printStackTrace();
}
}
更新磁盘块界面;
}
}
});
添加监听器;
设置字体;
设置文本框在命令行面板的中间;
创建一个滚动面板并加入文本框;
返回该滚动面板;
}

private JComponent catalog() throws Exception {
创建目录面板;
创建树组件;
根据根路径读取磁盘中的文件并构件树;
设置对应渲染;
tree.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
左键单击目录项则选中,更新选中节点,单机其他地方选中节点为空
if (点击左键) {
更新磁盘界面
if (选择区有结点) {
更新选中结点;
}else {
选中结点置空;
}
}
else if (点击右键) {
if (选择区有结点且是当前选中的结点) {
if(是根目录){
显示根目录菜单项;
}
else if (是文件)
显示文件菜单项;
else 显示普通目录菜单项;
} else {
选中结点置空;
}
}
if(选择结点不为空)
更新选中结点;
}
添加树组件进滚动面板并返回滚动面板;
}

//以字符串形式获取结点的路径
public String getPathString(DefaultMutableTreeNode node){
String path = “”;
String previous = “”;
if(结点为空)
return null;
while (结点有父结点) {
previous = path;
path = ‘/’ + node.getUserObject().toString() + previous;
node = (DefaultMutableTreeNode) node.getParent();
}
return path;
}

//构建目录树并返回根目录(广度优先搜索)
private DefaultMutableTreeNode getRootNode(String root){
创建字符串和结点队列;
把根路径和根节点加入两个队列;
while(字符串队列非空){
取出并移除队列头元素;
if(结点不是文件){
获取结点的孩子结点
if(孩子结点数量不为0){
for(遍历孩子结点){
根据孩子结点信息创建路径和结点;
并加入队列中;
}
}
}
}
返回根节点;
}
//显示文件的属性、即磁盘块占用情况
private void showDiskTable(Frame owner, Component parentComponent){
用getFileBlock获取当前目录项占用的磁盘块;
创建一个表格;
表头为"磁盘块",“值”;
设置字体间距;
根据磁盘占用情况构造表格;
设置对话框;
添加表格进对话框;
显示对话框;
}

//创建目录时候的弹出框及相关操作
private void showDirectoryCreation(){
try {
String inputContent = JOptionPane.showInputDialog(jf,
“新建目录命名:(要求小于三个字符)\n”,
“新建项命名”,3);
if(inputContentnull)
return ;
else{
int flag=1;
for(int i=0;i<inputContent.length();i++){
if(!Character.isUpperCase(inputContent.charAt(i))&&!Character.isLowerCase(inputContent.charAt(i))){
JOptionPane.showMessageDialog(jf, “目录名称有误!”, “提示”, JOptionPane.WARNING_MESSAGE);
flag=0;
break;
}
}
if(flag
1){
if(inputContent.length()0)
JOptionPane.showMessageDialog(jf, “目录名称不能为空!”, “提示”, JOptionPane.WARNING_MESSAGE);
else if(inputContent.length()>3)
JOptionPane.showMessageDialog(jf, “目录名称不能大于3个字符!”, “提示”, JOptionPane.WARNING_MESSAGE);
else {
int p = addDirectory(inputContent);
if(p
0){
updateImage();
view.updateUI();
}
else if (p == 2)
JOptionPane.showMessageDialog(jf, “磁盘已满,无法添加!”, “提示”, JOptionPane.WARNING_MESSAGE);
else if (p == 3)
JOptionPane.showMessageDialog(jf, “同级下有同名同类型文件,无法添加!”, “提示”, JOptionPane.WARNING_MESSAGE);
}
}
}
} catch (Exception exception) {
exception.printStackTrace();
}
}

//创建文件时候的弹出框及相关操作
private void showFileCreation(Frame owner, Component parentComponent) {
//创建文件时弹出的命名窗口
final JDialog dialog = new JDialog(owner,“新建文本命名”, true);
JPanel mainPanel =new JPanel(null);
JRadioButton radioBtn01 = new JRadioButton(“txt”);
JRadioButton radioBtn02 = new JRadioButton(“exe”);
ButtonGroup btnGroup = new ButtonGroup();
btnGroup.add(radioBtn01);
btnGroup.add(radioBtn02);
radioBtn01.setSelected(true);
JLabel label1 = new JLabel(“名称:”); label1.setFont(new Font(“宋体”,Font.BOLD,16));
JLabel label2 = new JLabel(“内容:”); label2.setFont(new Font(“宋体”,Font.BOLD,16));
JLabel label3 = new JLabel(“属性:”); label3.setFont(new Font(“宋体”,Font.BOLD,16));
JLabel label4 = new JLabel("(文本名称不大于3个字符)"); label4.setFont(new Font(“黑体”,Font.PLAIN,14));
JTextArea textArea=new JTextArea(“默认内容”); textArea.setFont(new Font(“宋体”,Font.BOLD,16));
textArea.setLineWrap(true); //自动换行
JScrollPane scrollPane = new JScrollPane(textArea,ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setViewportView(textArea);

JTextField nameField=new JTextField(); nameField.setFont(new Font("宋体",Font.BOLD,16));
JButton saveButton = new JButton("创建");
JButton cancleButton = new JButton("取消");
label1.setBounds(30, 30, 50, 30);
label2.setBounds(30, 130, 50, 30);
label3.setBounds(30, 80, 50, 30);
label4.setBounds(220, 30, 180, 30);
radioBtn01.setBounds(80,80,50,30);
radioBtn02.setBounds(170,80,50,30);
nameField.setBounds(80,30,140,30);
scrollPane.setBounds(80,130,220,100);
saveButton.setBounds(70, 250, 70, 30);
cancleButton.setBounds(270, 250, 70, 30);
mainPanel.add(saveButton);
mainPanel.add(cancleButton);
mainPanel.add(label1);
mainPanel.add(label2);
mainPanel.add(label3);
mainPanel.add(label4);
mainPanel.add(radioBtn01);
mainPanel.add(radioBtn02);
mainPanel.add(nameField);
mainPanel.add(scrollPane);
saveButton.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {  //创建
        String fileNameString=nameField.getText();
        String fileText=textArea.getText();
        int flag=1;
        for(int i=0;i<fileNameString.length();i++){
            if(!Character.isUpperCase(fileNameString.charAt(i))&&!Character.isLowerCase(fileNameString.charAt(i))){
                JOptionPane.showMessageDialog(jf, "目录名称有误!", "提示", JOptionPane.WARNING_MESSAGE);
                flag=0;
                break;
            }
        }
        if(flag==1){
            if(fileNameString.length()==0){
                JOptionPane.showMessageDialog(jf, "文件名称不能为空", "提示", JOptionPane.WARNING_MESSAGE);
            }
            else if(fileNameString.length()>3){
                JOptionPane.showMessageDialog(jf, "文件名称不能超过3个字符", "提示", JOptionPane.WARNING_MESSAGE);
            }
            else {
                try {
                    if (radioBtn01.isSelected())
                        fileNameString = fileNameString + ".txt";
                    else fileNameString = fileNameString + ".exe";
                    int p = addFile(getPathString(selectionNode)+'/'+fileNameString, fileText);
                    if(p==0){
                        updateImage();
                        view.updateUI();
                        dialog.dispose();
                    }
                    else if (p == 2)
                        JOptionPane.showMessageDialog(jf, "磁盘已满,无法添加!", "提示", JOptionPane.WARNING_MESSAGE);
                    else if (p == 3)
                        JOptionPane.showMessageDialog(jf, "同级下有同名同类型文件,无法添加!", "提示", JOptionPane.WARNING_MESSAGE);
                }
                catch (Exception exception) {
                    exception.printStackTrace();
                }
            }
        }
    }
});
cancleButton.addActionListener(new ActionListener() {  //取消
    @Override
    public void actionPerformed(ActionEvent e) {
        dialog.dispose();
    }
});
dialog.setSize(400, 350);
// 设置对话框大小不可改变
dialog.setResizable(false);
dialog.setLocationRelativeTo(parentComponent);
// 设置对话框的内容面板
dialog.setContentPane(mainPanel);
// 显示对话框
dialog.setVisible(true);

}

//显示打开文本的界面
private void showTxtFile(Frame owner, Component parentComponent) {
// txt文件显示和修改
String txtName=selectionNode.getUserObject().toString();
final JDialog dialog = new JDialog(owner,txtName,true);
String content= fileUtil.getFileContent(getPathString(selectionNode));
JTextArea textArea = new JTextArea(content);
textArea.setLineWrap(true);
textArea.setFont(new Font(“宋体”,Font.BOLD,24));
JScrollPane scrollPane = new JScrollPane(textArea);
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
scrollPane.setViewportView(textArea);

JPanel txtPanel = getBorderPane(scrollPane, 100, 60, 70, 60);
JPanel topPanel = new JPanel(null);
JPanel bottomPanel = new JPanel(null);
JButton saveButton = new JButton("保存");
JButton cancleButton = new JButton("取消");
JLabel label = new JLabel("文件内容");
label.setFont(new Font("黑体",Font.PLAIN,16));
label.setBounds(10, 5, 80, 20);
saveButton.setBounds(300, 10, 80, 30);
cancleButton.setBounds(420, 10, 80, 30);
bottomPanel.add(saveButton);
bottomPanel.add(cancleButton);
topPanel.add(label);
bottomPanel.setPreferredSize(new Dimension(0, 60));
topPanel.setPreferredSize(new Dimension(0, 30));
txtPanel.add(bottomPanel,BorderLayout.SOUTH);
txtPanel.add(topPanel,BorderLayout.NORTH);
saveButton.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        String newContent=textArea.getText();
        try {
            fileUtil.deleteFile(getPathString(selectionNode));
            fileUtil.createFile(getPathString(selectionNode),newContent);
            updateImage();
            view.updateUI();
        } catch (Exception exception) {
            exception.printStackTrace();
        }
        dialog.dispose();
    }
});
cancleButton.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        dialog.dispose();
    }
});
dialog.setSize(800, 600);
// 设置对话框大小不可改变
dialog.setResizable(false);
dialog.setLocationRelativeTo(parentComponent);
// 设置对话框的内容面板
dialog.setContentPane(txtPanel);
// 显示对话框
dialog.setVisible(true);

}

//删除文件或目录
private boolean deleteItem(String path) throws Exception {
boolean t=true;
int p=0;
if(选择目录项为空){
if(路径非空){
删除文件,t保存返回值;
if(tfalse) return false;
}
else return false;
}
if(选择结点是文件)
删除文件;
else 删除目录;
if(t
false||p==1) return false;
selectionNode.removeFromParent();
tree.updateUI();
selectionNode=null;
updateImage();
view.updateUI();
return true;
}

//新建目录
private int addDirectory(String newNodeString) throws Exception {
int p=fileUtil.makeDirectory(getPathString(selectionNode)+’/’+newNodeString);
if(p!=0)
return p;
DefaultMutableTreeNode newNode=new DefaultMutableTreeNode(newNodeString);
newNode.setAllowsChildren(true);
selectionNode.add(newNode);
TreeNode[] nodes = newNode.getPath(); //自动展开新建的目录
TreePath path = new TreePath(nodes);
tree.scrollPathToVisible(path);
tree.setSelectionPath(path);
selectionNode=newNode;
tree.updateUI();
return 0;
}

//新建文件
private int addFile(String newNodeString,String content) throws Exception{
int p=fileUtil.createFile(newNodeString,content);
if(p!=0)
return p;
newNodeString=newNodeString.substring(newNodeString.lastIndexOf(’/’)+1,newNodeString.length());
DefaultMutableTreeNode newNode=new DefaultMutableTreeNode(newNodeString);
newNode.setAllowsChildren(false); //不允许在文件下创建目录项
selectionNode.add(newNode);
TreeNode[] nodes = newNode.getPath(); //自动展开新建的目录
TreePath path = new TreePath(nodes);
tree.scrollPathToVisible(path);
tree.setSelectionPath(path);
selectionNode=newNode;
tree.updateUI();
return 0;
}

//设置当前选中的结点
private boolean setSelectionNode(String path,int type){
path=path.substring(1,path.length());
if(type1) //找要创建文件的父目录
path=path.substring(0,path.lastIndexOf("/"));
String []splits=path.split("/",-1);
DefaultMutableTreeNode node = null;
DefaultMutableTreeNode parentNode=rootNode;
for(String s:splits){ //遍历路径
for(int i=0;i<parentNode.getChildCount();i++){
node=(DefaultMutableTreeNode) parentNode.getChildAt(i);
s=s.trim(); //去掉字符中的空格,这步不做比较字符串会出错
if(node.getUserObject().toString().trim().equals(s)){
parentNode=node;
break;
}
if(i
parentNode.getChildCount()-1){
return false;
} //没找到相应结点
}
}
selectionNode=node;
TreeNode[] nodes = node.getPath();
TreePath paths = new TreePath(nodes);
tree.setSelectionPath(paths); //展开路径
return true;
}

3.2.3文件界面函数调用关系图

3.3 存储管理和设备管理
3.3.1 存储管理
(1)最佳适应算法
/**
* @param size
* @param id
* @Description 最佳适应算法
* @return
/
public boolean BestFit(int size,String id){
int findIndex = -1; //最佳分区的下标
//使min等于当前内存大小
int min = this.getSize();
for(对空间分配表循环查找){
Hole hole = this.getHoles().get(i); //取得空间分配表中当前下标i对应的内存块
if(内存块没有被占用&&内存块的大小大于申请内存的大小){
if(min大于 (当前内存块的大小-进程申请内存大小)){
min = hole.getSize();
findIndex = i ;
}
}
}
if(findIndex != -1) { //即有适合的内存块
this.getMemory(size, findIndex, id,this.getHoles().get(findIndex));
return true;
}
else {
System.err.println(“OUT OF MEMORY!”);
return false;
}
}
(2)申请内存
/
*
* @param size
* @param location
* @param id
* @param hole
* @return
/
public void getMemory(int size,int location,String id,Hole hole) { //size为申请大小 location为分配分区的下标 // hole为location对应的分区
if(hole.getSize() - size >= MIN_SIZE){
//建立一个新的空闲内存块节点,其起始地址为 hole.getHead() + size,大小为hole.getSize() - size
Hole newHole = new Hole(null,hole.getHead() + size, hole.getSize() - size);
//将新的内存块对象加入到location后面
holes.add(location + 1, newHole);
//改变当前内存块分区hole的大小为size
hole.setSize(size);
//将hole的id设为id
hole.setUid(id);
}
//添加一个进程
//id 进程名字
pcbs.add(new ProcessAddress(id, hole));
hole.setFree(false);
}
(3)内存回收
/
*
* @param id
* @return
*/
// 内存回收
public void releaseMemory(String id){
ProcessAddress pcb = null;
boolean flag = false;
for(对内存分配表进行遍历){
if(内存分配表中是否有id与传入的id相等){ //
pcb = pcbs.get(i);
flag = true;
break;
}
}
if(flag == false){
System.out.println(“无此分区:”+id);
}
if (pcb != null) {
for (int i = 0; i < holes.size(); i++) {
Hole hole = holes.get(i);
//寻找当前pcb在空间分配表中的位置下标
if ((pcb.getHole().getSize() == hole.getSize()) && (pcb.getHole().getHead() == hole.getHead())) {
id1 = i;
break;
}
}
}
Hole hole = holes.get(id1);
hole.setUid(null);
if(hole.isFree()){
System.out.println(“此空间空闲,无需释放:\t” + id);
}
for (int i = 0; i < pcbs.size(); i++){
ProcessAddress pcb2 = pcbs.get(i); if((pcb2.getHole().getHead()==hole.getHead())&&(pcb2.getHole().getSize()==hole.getSize())){
pcbs.remove(i);
break;
}
}
//判断后面分区是否为true,合并分区
if (id1 < holes.size()-1&&holes.get(id1+1).isFree()){
Hole nextHole = holes.get(id1 + 1);
hole.setSize(hole.getSize()+nextHole.getSize());
holes.remove(nextHole);
}
//判断前面分区是否为true,合并分区
if (id1 >0 &&holes.get(id1-1).isFree()){
Hole lastHole = holes.get(id1 - 1);
lastHole.setSize(hole.getSize()+lastHole.getSize());
holes.remove(id1);
id1–;

    }
        holes.get(id1).setFree(true);
        System.out.println("回收内存成功");

}
(4)存储管理函数调用关系图

3.3.2 设备管理
(1)申请设备
/**
* @Description 申请设备
* @param Uid
* @param time
* @param size
/
public int getDevice(String Uid, int time, int size ){
deviceWaitQueueA =new DeviceWaitQueue(Uid,size,time); //实例化设备队列对象
if(申请的设备有空闲设备)
如果有将空闲设备名字改为Uid
else
block.add(deviceWaitQueue);//将当前进程加入设备等待队列
返回设备号;
}
(2)释放设备
/
*
* @Description 释放设备
* @param Uid
/
public int[] removeDeviceA(String Uid){
int[] re =new int[2];
判断所有设备中是否有id与Uid相等
if(相等){
将设备名字改为“设备空闲“
判断设备等待队列是否为空
if(如果设备等待队列不为空)
gerFirstNode();
}
}
(3)获取设备等待队列第一个进程
/
*
* @Description 获取等待队列第一个节点
*/
public int gerFirstNodeA(){
deviceWaitQueue = block.removeFirst();
getDevice(deviceWaitQueue.getUid(),deviceWaitQueue.getTime(),deviceWaitQueue.getSize());
return deviceWaitQueue.getTime();
}
(4)设备管理函数调用关系图

3.4 进程管理
3.4.1 进程运行
public void cpu() throws InterruptedException{
while(文件没有执行完){
进程休眠;
if(进程发生调度){
恢复主要寄存器
}
处理中断;
if(不是闲置进程){
编译指令
}
}
}
private void psw1(){
保存最后运算结果;
调用进程调度销毁程序;
恢复主要寄存器;
}
private void psw2(){
进程转就绪队列;
恢复主要寄存器;
}
private void psw3(){
查找中断的设备
查找设备分配表找到进程uuid
psw3Util(uuid);
维护设备时间表;
if(运行中的进程为闲置进程){唤醒进程进入运行态}
else{加入就绪队列}
}
private void psw3Util(String uuid){
移出阻塞队列;
加入就绪队列或转为运行态
}
public void time(){
系统时间+1;
时间片-1;
if(时间片0) 设置时间片中断标志
设备时间-1;
if(设备时间
0) 设置设备终端标志
唤醒cpu进程
}
3.4.2 生成进程
createProcess(){
获取可执行文件列表
while(未执行完所有进程){
随机选择未执行的文件创建进程;
if(进程未满) {调用进程调度生成函数}
else conutine;
}
}

3.4.3 进程调度

public boolean create(byte[] file)
{
if(有充足内存空间) 创建进程占用内存空间
if(运行的进程==闲置进程) 进程转为运行态
}
public void destroy()
(){
释放内存;
if(就绪队列不为空)
从就绪队列队列取出pcb转为运行态
else
闲置进程转为运行态
进程数目-1
}
public int[] awake(PCB pcb)
{
释放设备;
维护设备时间数组;
}
public int block(String reason)
{
调用设备占用
维护设备时间数组;
}
3.4.4 工具类
public class Util {

/**
 * String文件转为byte文件
 * @param file
 * @return
 */
public static byte[] getByteFile(String file){
    String[] split = file.split("d");
    split[0] = split[0] + 'd';
    String[] split1 = split[0].split("\n");
    byte[] bytes = new byte[split1.length];
    for (int i = 0; i < split1.length; i++) {
        split1[i].replaceAll("\r","");
        bytes[i] = compile(split1[i]);
    }
    return bytes;
}

/**
 * byte文件转为String文件
 * @param file
 * @return
 */
public static String getStringFile(byte[] file){
    StringBuilder stringBuilder = new StringBuilder();
    for (Byte aByte : file) {
        String s = byteToString(aByte);
        stringBuilder.append(s);
        stringBuilder.append("\n");
    }
    return stringBuilder.toString();
}

/**
 * byte命令转为String命令
 * @param IR
 * @return
 */
public static String byteToString(byte IR){
    if (IR == 0){
        return "x++";
    }
    else if (IR == 32){
        return "x--";
    }
    else if(IR == 96){
        return "end";
    }
    else if(IR < 0){
        int i =IR + 128;
        return "x="+i;
    }else{
        int i = (int)IR;
        int code = i/64;
        int[] device = {(i % 64) / 16, (i % 64) / 8};
        int[] time = {i%16,i%8};
        if(device[code] == 0){
            return "!A" + time[code];
        }
        else if(device[code] == 1){
            return "!B" + time[code];
        }
        else{
            return "!C" + time[code];
        }
    }
}

/**
 * byte命令转为String命令
 * @param IR
 * @return
 */
public static byte compile(String IR){
    char[] chars = IR.toCharArray();
    if (IR.equals("x++") || IR.equals("X++")) {
        return 0;
    }else if (IR.equals("x--")||IR.equals("X--")) {
        return 32;
    }else if (IR.equals("end")||IR.equals("END")) {
        return 96;
    }else if (chars[0] == '!') {
        if(chars[2] == '8' || chars[2] == '9'){
            int b = 0;
            if(chars[1] == 'A' || chars[1] == 'a') {
                b = 0;
            } else if (chars[1] == 'B' || chars[1] == 'b') {
                b = 16;
            } else if (chars[1] == 'C' || chars[1] == 'c') {
                b = 32;
            }
            b = b + chars[2] - '0' ;
            return (byte) b;
        }else{
            int b = 64;
            if(chars[1] == 'A' || chars[1] == 'a')  {
                b+=0;
            } else if (chars[1] == 'B' || chars[1] == 'b') {
                b+=8;
            } else if (chars[1] == 'C' || chars[1] == 'c') {
                b+=16;
            }
            b = b + chars[2] - '0';
            return (byte) b;
        }

    }else {
        int i = 0;
        for (int j = 2; j < chars.length; j++) {
            i = i * 10 + chars[j] - '0';
        }
        i = i -128;
        return (byte)i;
    }
}

}

四、调试分析
(1) 调试过程中遇到的问题是如何解决的以及对设计与实现的讨论和分析
调试过程中遇到的问题:界面分布的设计问题,设备分配表的设计问题、Timer无法停止的问题。
进程运行问题:进程空指针问题;设备时间到未唤醒问题,第二次运行会加快进程的执行。
解决:空指针:闲置进程判断出错,导致对闲置进程的命令分析时报错,以及对中断的判断出错,导致运行中的进程跳过了end,文件超出数组范围。设备时间到未唤醒问题:没考虑到多个设备同时唤醒;第二次运行加快:cpu线程结束,没有把time线程也强制结束,导致第二次运行时,有两个time线程存在,唤醒cpu两次,在一个系统时间内执行了两条指令,之后指令的运行也越来越快。
(2) 算法的时间复杂性和改进设想
时间复杂度 O(n)
(3) 实现过程中出现的主要问题及解决方法
做文件界面时候遇到的第一个大问题就是如何正确调用到磁盘内部的接口。起初想到用swing的JTree结构,但是界面总是很难实时地调用到内部的接口,于是在磁盘内部设计一个可以返回当前目录子目录项字符串的函数,然后运用广度优先搜索的算法,由于递归的话传参会很麻烦,就创建两个队列,把字符串队列和树结点队列结合起来,就能通过根结点遍历到所有结点了。第二个问题是在创建、删除磁盘块的时候怎么实时更新磁盘块界面的图标。起初进了一个坑,就是每次创建删除都想着new新的组件,可是如果new了新组件,就必须把该组件加入到面板中,这样耗费了大量的资源,实际上只需要不断调用一个更新函数,让图标根据磁盘内部的结构更新,然后每次创建删除目录项时调用更新函数即可。
实现进程运行有几个问题:进程运行线程和时间线程之间的交互;如何知道设备时间已到,需要唤醒;如何唤醒和阻塞进程之后,维护设备时间数组;如何对指令进行编码使其只有一个字节;
解决:线程之间的交互使用了JUC的condition对象使其能够唤醒与顺序执行。设备的唤醒通过维护设备使用数组进行唤醒。维护设备时间数组通过阻塞与唤醒时返回的参数,确定正在使用设备的进程id以及时间等,特别是唤醒之后使用设备的进程。对指令编码,首先5个指令将8位数字前三位划分为指令数000、001、010、011、100,同时x=?是一位两位数,便将1后面的所有数字填入?。在阻塞队列中,又分为ABC三个设备,使用了两位用了设备标识,此时只剩三位,无法满足所需的时间需求。于是便将8-11的编码利用了剩余编码进行编码。
五、用户使用说明
1)桌面:进入初始化界面可选择“文件管理”图标进入文件管理界面或“进程”图标进入进程调度界面。

2)目录结构:文件的目录采用树形结构,考虑到该磁盘系统的空间有限,提供给用户的目录界面是全景式的,即可同时查看所有目录下的文件结构,目录下可以包含目录或txt和exe文件,但是文件不允许包含子目录项。

3)磁盘块视图更新:磁盘块共256块,每个盘块64字字节,0-3共4个磁盘块存放文件分配表,第4块存放根目录,其余磁盘块存放子目录和文件。磁盘块有3种显示方式,“未占用盘块”表示该磁盘块是空闲的,“被占用盘块”表示当前盘块被非选中的目录项或文件分配表占用,另一种则是当前目录项所占用的磁盘块,结构清晰,配有标注,用户能够很方便地知道每个目录项所占用的盘块。当用户对文件和目录进行删除、添加、修改文件内容等操作时,磁盘块界面会实时根据用户的操作更新磁盘占用情况。

4)添加文件:可添加的文件形式有txt和exe形式,用户右键点击想要添加文件的目录,然后单击“新建文件”,输入文件的名字,属性(txt或exe)和内容再确认即可。注意:文件长度不大于3个字符,磁盘块满了或同级目录下有同名文件则不允许添加,提示相关错误。

5)添加目录:用户右键点击想要添加目录的父目录,然后单击“新建目录”,输入目录的名字再确认即可。注意:目录长度不大于3个字符,磁盘块满了或同级目录下有同名目录则不允许添加,提示相关错误。

6)删除文件或目录:用户右键点击想要删除的文件或目录,点击“删除”即可。如果删除的是目录,则该目录下的所有子目录项被同时删除。

7)目录项属性:用户右键点击想要查看的目录项,点击“属性”,系统弹出目录项的属性框,显示文件或目录所占用的磁盘块号和盘块指向的下一个磁盘块号,当盘块的“值”为1时说明该磁盘块是当前目录项所占用的最后一个磁盘块。

8)打开及修改文件:用户右键点击想要打开的文件,选择“打开”,系统弹出文件的信息框,用户可直接在该提示框中查看或修改文件的内容(名字不可修改),点击“保存”保存文件内容或“取消”撤销修改操作。

9)命令行操作:程序启动时,默认用户没有管理文件权限,用户根据系统提示输出“cmd”则获得管理员权限,可对文件进行管理。管理模式下,用户输入“show instructions”或“si”可查看所有cmd命令,包括“创建文件”、“删除文件”、“打开文件”、“拷贝文件”、“建立目录”和“删除空目录”6种操作,如果用户输入的操作名或路径名有误,或是磁盘已满、目录下有同名同类型目录项等问题,提示错误。管理员模式下输入“exit”可退出到正常模式。

六、测试与运行结果
初始化界面:

文件系统初始界面:

新建文件夹及文件:


读写打开文件以及对文件进行写操作:

属性:

查看文件占用磁盘块:

删除:

命令行:

进程界面:
  • 26
    点赞
  • 72
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值