基于多道程序的单用户操作系统设计

OrdinAry

发布日期: 2019-02-25 15:31:07 浏览量: 1756
评分:
star star star star star star star star star_border star_border
*转载请注明来自write-bug.com

1 需求分析

  • 模拟操作系统实现。一个采用多道程序设计的单用户操作系统。包括进程管理、存储管理、设备管理、文件管理和用户接口

  • 本程序采用图形界面,输入主要为鼠标操作以及修改用户名、修改文件以及cmd中的键盘输入。输出则是对相应动作的响应

  • 输出的形式是,根据在桌面点击的图标,分别显示各模块内容。例如点击磁盘分配。会根据目前系统磁盘的使用情况显示。包括FAT表以及剩余磁盘块的饼状图显示

  • 文件管理

    • 文件逻辑结构采用流式结构
    • 文件均为文本文件,但分为可执行文件和不可执行文件
  • 磁盘管理

    • 文件对磁盘块是独占的
    • 目录结构采用树型目录结构
    • 每个目录项为8字节,要存储目录名或文件名,扩展名,目录、文件属性,起始盘号,文件长度。每个文件夹最多存放8个目录项
    • 根目录为磁盘第二块
    • 磁盘分配采用连接结构(显示连接),采用FAT记录磁盘空间使用情况和连接结构的指针
  • 设备管理

    • 管理一张设备分配表
    • 有A、B、C三种独占型设备。其中A设备2个,B设备3个,C设备3个
    • 不考虑死锁
    • 设备使用完后立即释放该设备资源
  • 存储管理

    • 用链表模拟内存空间分配表
    • 系统区存放进程控制块和内存分配表
    • 用户区存放可执行文件
  • 进程管理

    • 首先随机创建10个可执行文件来进行调度
    • 采用时间片轮转调度算法,时间片长度为6
    • 进程控制块包括进程标识符、主要寄存器、进程状态、阻塞原因。本系统最多容纳10个进程块
  • 用户接口

    • 用户可以通过鼠标和键盘来访问和操作该操作系统提供的各种功能。模拟Windows的命令行

2 概要设计

2.1 磁盘管理

  • 主要涉及到的类有:Disk、FAT

  • 功能:磁盘容量为256个磁盘块,每个磁盘块有64字节。根据FAT来管理磁盘块的分发和回收。

  • 磁盘管理根目录root,提供磁盘格式化方法。

  • FAT管理磁盘块,提供分配、回收、显示磁盘块使用情况等方法。

2.2 文件管理

  • 主要涉及到的类有:FileItem、Files、Directory

  • 功能:提供文件和文件夹对象。以及对文件和文件夹的一系列操作。

2.3 设备管理

  • 主要涉及到的类有:Device、DeviceManager

  • 功能:管理设备,提供设备分配,回收以及查看设备使用情况的方法

2.4 存储管理

  • 涉及的包:model.momory

  • 功能:管理内存,提供内存分配、内存释放以及查看内存的方法

2.5 进程管理

  • 涉及的包:model.cpu,view.cpu

  • 功能:管理进程的创建、删除以及调度,提供创建进程的方法。

2.6 图形界面(用户接口)

  • 涉及的包:view.ui,utility

  • 功能:响应用户的鼠标以及键盘输入。

3 详细设计

3.1 存储管理(后台数据)

  • Memory类

    • 表示内存
    • 因为全局只有一个内存,故采用单例模式
    • 属性:LinkedList<MemoryBlock>blocks
    • 链式存储,故用链表存下内存块,维护内存结构
    • 方法实现
      • MemoryBlock allocate(MemoryOccupyoccupy);分配内存,采用首次适配;遍历内存块链表,找到第一个长度大于occupy.length()的内存块,将其拆成两段,第一段长度等于occupy.length(),并设置为已经占用状态,如果有第二段(剩余),则设置第二段长度为剩余的长度;如果找不到可分配的内存块,返回null。
      • void release(MemoryBlockreleaseBlock);内存释放;找到releaseBlock在内存块链表里对应的位置,找不到时抛出异常;将releaseBlock设置为未占用状态,判断releaseBlock前后两个内存块(如果有),如果有未占用状态的,将其与releaseBlock合并。
  • MemoryBlock类

    • 表示内存块
    • 属性
      • int startPosition,内存块起始位置
      • int length,内存块长度
      • boolean isEmpty,是否被占用
  • MemoryOccupy接口

    • 内存占用接口
    • 方法:int length(),返回占用内存的大小

3.2 进程管理(后台数据)

  • CPU类

    • 负责进程调度
    • 属性
      • static int MAX_NUMBE_OF_PROCESS,最大进程数,默认值为11(10个普通进程,1个闲逛进程)
      • LinkedBlockingQueue<PCB> readyQueue,就绪队列
      • PCB runningProcess,运行中的进程的PCB
      • Memory memory,内存
      • PCBManager pcbManager,PCB管理器
      • PCB strollingProcess,闲逛进程的PCB
      • static int TIME_SLICE,轮转时间片,默认值为3
      • static int INS_UNIT,每条指令执行的时间,单位个(时钟周期),默认值为3
      • InsExecutor insExecutor,指令执行器
      • DeviceManager deviceManager,设备管理器
      • enum Result,枚举类,用于创建进程的反馈,属性如下
        • OK,成功
        • COMPILE_ERROR,编译失败
        • PCB_NOT_ENOUGH,无可用PCB
        • MEMORY_NOT_ENOUGH,内存不够
    • 方法
      • Result create(String instructions),根据指令字符串instructions创建进程,创建失败时返回原因
      • void destroy(),撤销当前进程,回收PCB及内存
      • void block(),阻塞当前进程,交由DeviceManager处理
      • void awake(PCB pcb),唤醒pcb对应的进程,会将pcb加到就绪队列
      • Result allocatePCB(ProcessCode code),分配PCB。当有可分配PCB且有足够内存时,分配PCB;否则返回分配失败原因
      • void work(),每INS_UNIT个时钟周期调用一次handle()
      • void handle()
        • 调用CPU.insExecutor.execute()执行当前指令
        • 当寄存器状态为PSW_Type.TIME_OUT时,将运行中进程放到就绪队列尾部,并将就绪队列头的进程置为运行态;
        • 当寄存器状态为PSW_Type.IO_INTERRUPT时,阻塞当前进程并将就绪队列头的进程置为运行态;
        • 当寄存器状态为PSW_Type.END时,撤销运行中进程并将就绪队列头的进程置为运行态
  • CPURegisters类

    • 表示CPU寄存器
    • 属性
      • num PSW_Type,枚举类,属性如下
        • NOTHING,无特别标志的状态
        • END,表示当前进程运行结束
        • TIME_OUT,表示当前进程运行时间片结束
        • IO_INTERRUPT,表示当前进程需进入阻塞状态
        • int AX,进程的x变量的值
        • PSW_Type PSW,进程当前状态
  • InsExcutor类

    • 管理进程的指令的执行
    • 属性
      • ProcessCode code,运行中的进程的代码
      • CPURegisters registers,CPU的寄存器
      • int timeLeft,当前进程剩余时间片
      • char deviceID,如果当前进程申请I/O,该变量值为进程申请的设备号
      • int deviceTime,如果当前进程申请I/O,该变量值为进程申请占用设备的时间
    • 方法实现
      • void execute();执行当前指令;先使剩余时间片减少1,减少后,如果剩余时间片等于0,则设置寄存器标志位PSW_Type.TIME_OUT,接着调用code.getIns()获得当前指令,根据指令类型改变registers.AX或registers.PSW的值
  • PCBManager类

    • 负责PCB的分配
    • 属性
      • ConcurrentHashMap<Integer, PCB> map,保存pid到PCB的映射
      • LinkedBlockingQueue<Integer> queue,未被使用的pid的队列
    • 方法
      • int getNewPID(),从PCBManager.queue获得新的pid,当没有可用pid时(最多有10个进程同时运行),返回-1
      • PCB allocatePCB(MemoryBlock block, ProcessCodecode),分配新的PCB,失败时返回null
      • PCB getPCB(int pid),根据pid获得对应PCB
      • void recoverPCB(PCB pcb),回收PCB
      • boolean available(),返回当前是否有可分配的PCB
      • int getPID(MemoryBlock block),根据内存块获得对应pid,当找不到对应的pid时,返回-1
  • SystemClock类

    • 模拟系统时钟,每隔一个时钟周期,会调用一些函数,如调用CPU.work()、DeviceManager.work()等;全局只有一个系统时钟,故用单例模式
    • 属性
      • int unit = 1000,系统时钟周期,单位(毫秒)
      • long initTime,创建实例时的时间,用于计算获得系统时钟
      • LinkedBlockingQueue<Runnable> runnables,存下需要在每个时钟周期都被调用的Runnable
      • Timer timer,用于执行TimerTask
    • 方法
      • void addEvent(Runnable runnable),添加一个任务,每隔一个时钟周期会自动调用runnable.run()
  • Compiler类

    • 提供对进程指令的编译方法
    • 编码规定
      • x=?
      • 必须是[0, 99]的数字,编码为对应数字
      • x++,编码为100
      • x—,编码为101
      • 第一个?必须是A、B或C,第二个?必须是[1, 9]的数字,编码为[111, 139]的数字,其中十位表示设备,1A,2B,3C对应,个位表示时间
      • end,编码为Compiler.END_CODE,默认为200
      • 编译失败,编码为255
    • 方法实现
      • static ProcessCode compile(Stringinstructions);参数instructions是保存了多个指令的字符串,指令间用空格或回车或制表符隔开;先使用String.split(“[\s]”)将多个指令分割成单个指令的String数组,遍历该数组,对于每条指令ins,调用Compiler.encode(String),如果encode()返回值等于Compiler.END_CODE,则编译失败,返回null,否则将encode()返回值收集到一个数组,接着使用该数组生成对应的ProcessCode并作为返回值
      • static int encode(String code);根据编码规定,逐一判断code属于哪种类型指令,返回对应编码
  • PCB类

    • 进程控制块,保存进程ID、进程占用的内存块,运行时的上下文(寄存器)
    • 属性
      • int id,进程ID
      • MemoryBlock memoryBlock,进程对应的内存块
      • CPURegisters registers,上下文(寄存器)
      • ProcessCode code,进程指令
  • CodeBuilder类

    • 用于随机生成进程指令代码
    • 属性
      • enum CodeType,枚举类,表示指令类型,属性如下
        • Assignment,赋值型指令
        • Increase,自增指令
        • Decrease,自减指令
        • IO,申请I/O指令
    • 方法实现
      • static String getRandomCode();获得单个随机指令;使用Random.nextInt()获得随机的指令类型下标,根据对应的类型生成对应的指令,如果是赋值指令或IO指令需要再次随机得到对应参数
      • static String getRandomCodes();获得多个随机指令,先用Random.nextInt()获得随机的指令个数,再根据个数多次调用CodeBuilder.getRandomCode()得到多个随机指令,最后将这些指令用回车拼接到一个字符串并作为返回值
  • ProcessCode类

    • 保存进程的指令代码,以及进程运行的当前指令
    • 属性
      • int[] instructions,保存多个编码后的指令
      • int index,保存当前执行的指令的下标

3.3 设备管理(后台数据)

  • Device类

    • 表示设备
    • 属性
      • char device_ID,设备的ID
      • int remainTime,在此设备上运行的进程的剩余时间
      • boolean free,设备是否空闲
      • intindex_of_usingProcess,进程表中该设备的索引
      • PCB pcb,在此设备上运行的进程的PCB
    • 方法实现
      • voidrun(ArrayList<Pair<Integer, Integer>> usingProcess);设备运行;在进程运行表中不断更新该设备的信息,并且使设备运行时间每次减1,当为0时,回收设备,当有其他进程等待该设备时,分配给其他进程
  • DeviceManager类

    • 设备管理器,负责设备的分配和回收
    • 属性
      • static DeviceManagerdeviceManager,单例模式创建设备管理器对象
      • static int MAX_NUMBER_OF_A_DEVICE = 2,设置A类设备个数
      • static int MAX_NUMBER_OF_B_DEVICE = 3,设置A类设备个数
      • static int MAX_NUMBER_OF_C_DEVICE = 3,设置A类设备个数
      • ArrayList<Pair<Integer,Integer>> usingProcess,进程使用表,记录设备的设备号以及正在使用该设备的进程的进程号
      • HashMap<Character,ArrayList<Device>> map_of_device,各类设备的集合表
      • HashMap<Character, Queue<PCB>>map_of_pcbQueue,阻塞进程的队列
      • HashMap<PCB, Integer> map_of_time,阻塞进程的运行时间
      • HashMap<Character,Queue<Device>>map_of_freeDeviceQueue,各类空闲设备的队列
    • 方法实现
      • void request(PCBpcb, char device_ID, int time);分配设备给进程;如果该类设备有空闲设备,则分配一个给该进程;如果没有,则将该进程放入阻塞进程队列,并保存相关数据
      • void release(Devicedevice);回收设备;将该设备放入空闲设备队列,并清空该设备数据,置为空闲,将该设备从进程使用表中移除
      • void occupy(chardevice_ID);占据设备;检测请求该类设备的阻塞进程队列是否为空,若不为空,则队列头进程出队,占据一个该类设备,调用分配设备方法
      • void work();设备工作;扫描所有设备,如果设备非空闲,则令设备每个工作时间运行一次,进程剩余时间减1,更新进程使用表

3.4 文件管理(后台数据)

本系统中,文件类型有:文件夹、文本文件、可执行文件

主要用到的类有FileItem,Directory,Files

3.4.1 FileItem

  • 该类为所有文件类的父类

  • changeFilesName(StringfileName,String newFileName,String fileExtentionName)

    • 首先判断新文件名的合法性
    • 遍历父文件下同类型所有文件的文件名,判断是否存在同名文件
    • 返回修改是否成功的代码,其中
      int:0:修改成功,1:文件名字数超出限制,2:文件名已存在,3:文件名含非法字符,4:文件名不得为空
  • deleteFiles()

    • 首先判断文件是否可以删除
    • 如果是文件,直接删除并回收磁盘块
    • 如果是文件夹,递归删除其中的所有文件与文件夹
  • isDirectory()

    • 判断所给的FileItem是否为Directory

3.4.2 Files

  • 该类为文件类,文件有文本文件和可执行文件两种

  • changeFilesContent(String content)

    • 修改文件内容
    • If(只读)
      • 返回
    • Else
      • 计算修改后占用磁盘块数,与空闲磁盘块数比较
      • 修改占用的磁盘块情况
    • 返回 int,其中:
      0:修改成功,1:只读属性文件不可修改,2:磁盘空间不足
  • deleteFiles()

    • if(文件可删除)
      • 删除文件
      • 回收磁盘块
  • isTxtFile()

    • 文件类型是可执行还是文本类型

3.4.3 Directory

  • 该类为文件夹类,每个文件夹,管理一个容量为8的文件数组

  • isFull(),判断文件夹是否已满

  • createTxtFile()和createExeFile()分别创建文本文件和可执行文件

  • createFile(String fileName,StringfileExtentionName,int capacity,int attribute,String content)根据参数创建文件

  • createDirectory()创建文件夹

  • createFileName()创建文件名

  • isExistedName()判断文件名是否已存在

  • removeFiles(FileItem f)删除文件夹下的某个文件

  • findFiles(String fileName)通过文件名来查找文件或文件夹

  • deleteFiles()删除整个文件夹

  • getAllFilesName()获取该目录下所有文件夹和文件的名称

3.5 磁盘管理(后台数据)

  • 磁盘管理部分管理文件在磁盘中的存取,磁盘块的分配与回收。

  • 主要用到的类有Disk与FAT类

3.5.1 Disk类

  • 该类为单例模式,因为磁盘只有一个。磁盘应由多个磁盘块组成,为了简化程序,用FAT中的数组来代替组成磁盘的磁盘块。并且将磁盘块的存储和读取等功能分到文件等别的类上。

  • Init()

    • 创建根目录,并设置为不可删除
  • Format()

    • 该函数为格式化磁盘,用递归的方法将根目录下的所有文件与文件夹删除,并返回删除是否成功.

3.5.2 FAT

  • 该类为单例模式。

  • Init()

    • 初始化数组容量为磁盘最大磁盘数。
    • 前三项值为-1(表示已占用),其他全为0(表示空闲)。
  • changeFAT(int capacity)

    • 修改FAT,capacity为待分配文件的大小。
    • 根据capacity算出所需磁盘块。
    • 从第三块磁盘块开始遍历所有磁盘块
    • If(磁盘块内容为0&&起始磁盘号==-1)
      • 做好标记
    • Else(fat[last] =i)
      • 最后一个磁盘块内容为-1
    • 返回起始磁盘块下标
  • Recovery(int startNumber)

    • 根据起始磁盘块号
    • While(fat[num]!=-1)
      • 记录下一个磁盘块下标
      • Fat[num] = 0
  • capacityOfDisk()

    • 计算fat中值为0的数据个数从而得出磁盘容量。
  • GetNext(int number)

    • 获得下标为number的磁盘块的值。
  • getFreeBlocks()

    • 返回一个数组,存放的是空闲磁盘块的下标,用于显示磁盘块占用情况。
  • getFAT()

    • 返回复制得到的磁盘块数组(为了保护真正的磁盘块数组)。
  • Append(int startNum,intaddNumOfBlocks)

    • 追加磁盘块,主要用于修改文件内容后保存时,是在原来的基础上追加空闲的磁盘块,而不是重新分配。
  • Remove(int startNum,int numOfBlocks,intnowNumOfBlocks)

    • 回收磁盘块,主要用于修改文件后,文件占用磁盘块减少时,回收原来占用的磁盘块的后几个。

3.6 CMD(后台数据)

  • 主要涉及到的类是CmdUtil,用于提供各种操作给前端的用户命令行

3.7 用户图形界面(前端界面)

3.7.1 界面设计

ui包的类目录结构

  • Console作为命令行控制类,对用户输入的命令执行相应的操作。

  • Controller 协调文件管理系统和cmd界面之间的关系,根据用户的操作,反馈到文件上

  • CreateWindows 提供给文件系统创建窗口的接口,以便图形化管理。

  • Icon桌面的图标,实现监听,生成对应的窗口。

  • IconManager 对图标的管理以及保存单个窗口的实例,如cmd、help等窗口

  • MainPane 作为桌面底部的容器pane,存放桌面的信息和图标

  • MsgWindow 信息窗口,每次关闭文件窗口或者是其他有需要反馈给用户的弹窗

  • RootPane 最顶层的面板容器,存放着MainPane以及TaskBar

  • SecondaryMenu 邮件菜单栏,存放着刷新等功能。

  • TaskBar 系统的底部任务栏,管理着窗口的显示和销毁

  • Window 实际每次生成的窗口类,包含着不同类型的窗口

  • WindowMenu 和TaskBar相互协调对窗口进行相应的管理。

Console过滤用户的输入

  1. //对输入窗口中的文本值改变时的监听器
  2. {
  3. boolean isInSide 判断是否按下回车初始值为false
  4. @Override
  5. 重写change函数{
  6. if(没有按下回车并且不是为了生成用户的反馈信息){
  7. if(文件的值最后一行包括回车符时){
  8. if(新值的最后一行的长度除去当前目录不为空时){
  9. 把处理过后值传入Controller中进行命令的判定
  10. }
  11. 把当前的文本值加上路径;
  12. 更新preCount的最后一行前的字符数;
  13. 移动光标到最后一行;
  14. 设置滑条到滑条的底部;
  15. }else{
  16. if(如果最后一行没有包括目录(及用户想删除当前目录名时)){
  17. 保留旧值;
  18. 移动光标到最后一行;
  19. 设置滑条到滑条的底部;
  20. }
  21. }
  22. isInSide取反
  23. }
  24. });

Controller对用户输入命令的判定

  1. analyseRoute()//用于分离Console传入的文本指令的类词法分析器
  2. {
  3. 用正则表达式过滤掉空格和小数点;
  4. 返回一个LinkedList<String>保存指令不同的输入词汇;
  5. }
  6. setMessage() //用于analyseRoute函数之后,进行命令的判定
  7. {
  8. 根据链表的长度以及相应位置的数值进行判定;
  9. 跳转到相应的case里面:
  10. if(如果对应到某条指令)
  11. 调用文件管理器的工具类执行相应的操作
  12. else
  13. 返回相应的错误提示,显示到Console的文本上
  14. }

CreateWindows生成窗口

  1. create()//方法进行重载
  2. {
  3. 根据传入的参数file,打开对应的窗口,file只能为.txt或.exe结尾的文件
  4. }
  5. create()//方法进行重载
  6. {
  7. 根据传入的参数string,生成对应的信息窗口,做出一定的用户反馈
  8. }

Icon 桌面图标类

  1. //本身继承标签类
  2. private Image image; //图标的图片
  3. private ImageView imageView; //图片的容器
  4. private Border stroke; //边框对象
  5. private Window window; //图标对应的窗口对象
  6. // private String fileName; //图标的名字
  7. private Type type;//图标对应的类型
  8. init() //图标的类初始化函数
  9. {
  10. 边框的设置;
  11. 鼠标点击时事件的监听:生成对应的窗口;
  12. 鼠标进入图标区域以及离开图标区域时颜色的变化监听;
  13. }

IconManager 管理器

  1. private Icon[] icons; //保存桌面的图标对象
  2. privateWindow[] windows; //保存唯一窗口cmd、help等窗口对象
  3. initIcon() //初始化桌面图标,并显示

MainPane 桌面容器

  1. init()//作为初始化函数
  2. {
  3. 设置大小;
  4. 鼠标点击时事件,其他图标和窗口执行对应的操作;
  5. 鼠标右键生成菜单栏
  6. }
  7. reStart()// 多线程刷新页面用于实现右键的刷新效果

MsgWindow 信息窗口类

用于文件编辑时执行相应的提示和操作的选择。

RootPane 作为顶层容器

SecondaryMenu 鼠标右键栏

  1. init()//初始化操作
  2. {
  3. 设置对应标签的位置;
  4. 设置鼠标点击每个标签时的事件;
  5. }
  6. display()//用于兼容鼠标右键多次点击时位置的更新以及图标的右键菜单

TaskBar 底部任务栏

  1. private static ArrayList<Window> windows; //保存所有的窗口
  2. private static ArrayList<WindowMenu> menus; //保存所有类型的窗口对应的窗口列表
  3. private static ArrayList<Label> labels; //保存不同类型窗口对应的图标

构造函数初始化时,生成所有的相应的label和menus对象一一对应,做好相应的监听环节,一旦有窗口生成,则底部的任务显示对应的图标和窗口列表

  1. addWindow() // 加入对应的窗口,传入的参数为窗口对象以及窗口的名字
  2. removeWindow() // 删除指定的窗口,同时根据窗口数量更新任务栏的显示。
  3. canselMenu() //调节窗口列表的显示和消失

Window 用于各种不同窗口的生成

根据传入的Type类型,生成对应的窗口。

  1. init() // 执行每一种窗口类型相同参数的设置。
  2. createCMDWindow()、createTxtWindow() 等函数用于设置不同窗口的不同参数

WindowMenu 窗口列表,用于同一类型的窗口,存在多个时的选择问题

  1. private Type type; //保存当前窗口列表的类型
  2. private staticArrayList<EventHandler<MouseEvent>> handlers; //保存每一种类型窗口

执行的操作

  1. addLabel() //同一种窗口生成相应的列表文件
  2. removeLabel() //删除对应menus里面的某一行,代表移除窗口
  3. initHandlers() //初始化监听事件

3.8 信息窗口(前端界面)

  • 本包括进程、设备、磁盘以及内存的信息显示

  • 使用FXML进行界面设计

  • 大致界面:

  • 涉及的类:CPUWindowController,控制器,前端与后台的桥梁

  • 界面的刷新:

利用SystemClock.addEvent(),每隔一个时钟周期,调用CPUWindowController.update()进行界面刷新

4 测试与运行结果

cmd窗口生成文件时,磁盘空间和文件管理器产生相应的变化

生成相同类型窗口时底部任务栏和窗口列表的显示

上传的附件 cloud_download 基于多道程序的单用户操作系统设计.zip ( 8.48mb, 104次下载 )
error_outline 下载需要9点积分

发送私信

有没有那样一种永远,永远永远不改变

12
文章数
32
评论数
最近文章
eject