操作系统课程设计报告.docx
《操作系统课程设计报告.docx》由会员分享,可在线阅读,更多相关《操作系统课程设计报告.docx(18页珍藏版)》请在冰豆网上搜索。
![操作系统课程设计报告.docx](https://file1.bdocx.com/fileroot1/2023-1/12/0f1b0104-7cf0-4357-a524-02aa8cccf2bc/0f1b0104-7cf0-4357-a524-02aa8cccf2bc1.gif)
操作系统课程设计报告
《操作系统课程设计》报告
专业班级:
计科1106班
姓名:
李育洪、胡壮
刘春林、邓程峰
指导老师:
李玺
设计时间:
2014年6月
第一章概论·······································3
第二章设计的基本概念和原理·······················4
第三章总体设计···································6
第四章详细设计···································8
第五章系统的测试和运行···························18
第六章系统的使用说明·····························20
第七章课程设计总结·······························21
第八章参考资料···································22
第一章
概论
1.1课程设计的内容
本次课程设计我们是四个人的小组,我们选择的是第三个题目,题目的描述如下:
在uC/OS操作系统中增加一个简单的文件系统,要求如下:
(1)熟悉并分析uc/os操作系统
(2)设计并实现一个简单的文件系统
(3)可以是存放在内存的虚拟文件系统,也可以是存放在磁盘的实际文件系统
(4)编写测试代码,测试对文件的相关操作:
建立,读写等
1.2课程设计的目的
操作系统课程主要讲述的内容是多道操作系统的原理与技术,与其它计算机原理、编译原理、汇编语言、计算机网络、程序设计等专业课程关系十分密切。
本课程设计的目的综合应用学生所学知识,建立系统和完整的计算机系统概念,理解和巩固操作系统基本理论、原理和方法,掌握操作系统开发的基本技能。
1.3要解决的主要问题
通过对题目的分析,以及对相关资料的查阅,我们决定为μC/OS-II写一个FAT32文件系统。
那么,我们要解决的主要问题就有:
1)掌握μC/OS-II的基本原理,并能在μC/OS-II上用C语言进行程序设计;
2)μC/OS-II开发环境的建立。
由于我们没有嵌入式的硬件设备,所以在PC上进行开发。
于是就需要把μC/OS-II一直到windows下,用VC++作为开发环境;
3)了解FAT32文件系统的底层细节。
必须要有FAT32这种文件系统的详细说明。
4)手动实现FAT32文件系统。
5)测试。
必须保证文件系统能在μC/OS-II下运行。
第二章设计的基本概念和原理
2.1μC/OS-II简介
μC/OS-II由Micrium公司提供,是一个可移植、可固化的、可裁剪的、占先式多任务实时内核,它适用于多种微处理器,微控制器和数字处理芯片(已经移植到超过100种以上的微处理器应用中)。
同时,该系统源代码开放、整洁、一致,注释详尽,适合系统开发。
μC/OS-II已经通过联邦航空局(FAA)商用航行器认证,符合航空无线电技术委员会(RTCA)DO-178B标准。
μC/OS-II可以大致分成核心、任务处理、时间处理、任务同步与通信,CPU的移植等5个部分。
1)核心部分
是操作系统的处理核心,包括操作系统初始化、操作系统运行、中断进出的前导、时钟节拍、任务调度、事件处理等多部分。
能够维持系统基本工作的部分都在这里。
2)任务处理部分
任务处理部分中的内容都是与任务的操作密切相关的。
包括任务的建立、删除、挂起、恢复等等。
因为μC/OS-II是以任务为基本单位调度的,所以这部分内容也相当重要。
3)时钟部分
μC/OS-II中的最小时钟单位是timetick(时钟节拍)。
任务延时等操作是在这里完成的。
4)任务同步和通信部分
为事件处理部分,包括信号量、邮箱、邮箱队列、事件标志等部分;主要用于任务间的互相联系和对临界资源的访问。
5)与CPU的接口部分
是指μC/OS-II针对所使用的CPU的移植部分。
由于μC/OS-II是一个通用性的操作系统,所以对于关键问题上的实现,还是需要根据具体CPU的具体内容和要求作相应的移植。
这部分内容由于牵涉到SP等系统指针,所以通常用汇编语言编写。
主要包括中断级任务切换的底层实现、任务级任务切换的底层实现、时钟节拍的产生和处理、中断的相关处理部分等内容。
2.2μC/OS-II在VC++下的移植
为了开发环境的方便,需要将μC/OS-II移植到VC++上。
移植主要有三步,下面作简要说明。
详细的方法可以参考《嵌入式实时操作系统μC/OS原理与实践》一书。
1)VC下时钟的获得
可以使用采用软件定时器来模拟时钟中断。
这里我们采用的是timeSetEvent()函数。
这个函数很简单,不需要消息循环,定时精度为ms级,主要应用在多媒体定时方面,能够在非常精确的时间间隔内完成一个事件、函数或过程的调用。
可以通过调用timeSetEvent()函数,将需要周期性执行的任务定义在LpTimeProc回调函数中,从而完成所需处理的事件。
调用这个函数后会增加一个线程,时间一到则在这个线程中调用回调函数,对于主线程来说,非常类似外部中断调用,我们需要的正是这样的效果。
2)模拟时钟中断的产生
中断指的是中止当前的事务,处理别的更要紧的事情。
我们通过软件定时器来模拟产生uC/OS-II的时钟中断,但timeSetEvent()函数调用定时回调函数是和主线程同时被windows操作系统调度的,并没有起到中断的作用。
所以在调用定时回调函数的时候必须停止主线程的运行,退出回调函数则恢复主线程的运行,自然这些事情可以都放在定时回调函数,也就是uC/OS-II的时钟中断处理函数中完成。
Windows下要挂起一个线程的运行,首先要得到这个线程的句柄,然后调用SuspendThread(hangdler)和ResumeThread(handler)就可以挂起和继续执行线程。
3)任务切换
任务切换,其实做的是任务的上下文切换,在其他CPU上非常容易分辨出任务的上下文,一般就是CPU上的相应寄存器,那么在VC下呢?
从简单考虑,我们选择了不带浮点运算的上下文环境,因此任务的上下文和uC/OS-II在80x86上移植的上下文很相近,不同点只是段寄存器不用保存,因为在VC下任务其实只是在同一个线程中切换,而且在保护模式下段寄存器的概念已变,其值在同一个线程中是不会变的。
2.3FAT32文件系统格式
FAT32是Windows系统硬盘分区格式的一种。
这种格式采用32位的文件分配表,使其对磁盘的管理能力大大增强,突破了FAT16对每一个分区的容量只有2GB的限制。
由于现在的硬盘生产成本下降,其容量越来越大,运用FAT32的分区格式后,我们可以将一个大硬盘定义成一个分区而不必分为几个分区使用,大大方便了对磁盘的管理。
目前已被性能更优异的NTFS分区格式所取代。
一个FAT文件系统包括四个不同的部分:
保留扇区、FAT区域、根目录区域、数据区域。
对于FAT32的具体说明可以参考微软官网上关于FAT32的白皮书,网址:
。
第三章总体设计
嵌入式文件系统由于功能和作用与普通桌面操作系统的文件系统不同,导致了二者在体系结构上具有很大的差异性。
在普通桌面操作系统中,文件系统不仅要管理文件,提供文件系统调用API,还要管理各种设备,支持对设备和文件操作的一致性(即要像操作文件一样来操作各种I/O设备)。
在嵌入式文件系统中,这种规则发生了很大的变化。
在某些情况下,嵌入式系统可以针对特殊的目的来进行定制,特别是随着ASOS(为应用定制的嵌入式操作系统)的发展,对嵌入式操作系统的系统功能规整性、可伸缩性及其灵活性提出了更高的要求。
基于以上的考虑,我们采用了下图所示的嵌入式文件系统体系结构,该结构定义的文件系统从上到下有三个层次:
第一层为API层、第二层为中间转换层、下层为介质驱动层。
第一层:
API层。
API层是文件系统和用户应用程序之间的接口,它有一个标准C函数库,其中包含有诸如打开文件(f_open)、写文件(f_write)等函数。
本层的功能是将用户调用传送给中间转换层。
这是整个系统设计的核心,也是嵌入式文件系统中用户唯一可见的部分。
第二层:
中间转换层。
中间转换层要为文件系统的实现提供与硬件无关的统一接口,是文件系统结构规整性的基础。
中间转换层包含有文件系统子层及逻辑块子层,其中文件系统子层将文件操作解释到逻辑块子层,然后文件系统调用逻辑块子层并根据不同的设备定义出相应的设备驱动程序;逻辑块子层主要是同步对设备驱动程序的访问,向上提供友好界面。
第三层:
介质驱动层。
介质驱动层是访问硬件的最低端的程序,该程序的结构要能够便于实现对硬件的访问。
本层的功能主要是完成对介质的访问。
本层的重要任务就是提供统一的设备驱动程序接口。
根据文件系统的层次结构,可以将该文件系统分成三大功能块:
API接口模块、中间转换模块、设备驱动模块。
其中API接口模块主要完成文件的基本操作,包含有文件的生成、删除、打开、关闭、文件读、文件写等。
中间转换模块主要完成对存取权限的检查、介质的选择、逻辑到物理的转换。
设备驱动模块完成存储介质的驱动程序,包含有一个驱动程序函数表和介质读、介质写、检查状态、执行特定命令等驱动程序。
第四章详细设计
总体设计完成之后,就可以几种精力进行详细设计。
根据总体设计划分的模块,一个模块一个模块进行详细设计。
4.1文件系统对外提供的主要接口
1、FRESULTf_open(FIL*,constchar*,BYTE);
函数功能:
打开或者创建一个文件
2、FRESULTf_read(FIL*,BYTE*,WORD,WORD*);
函数功能:
读一个文件
3、FRESULTf_close(FIL*);
函数功能:
关闭一个文件
4、FRESULTf_opendir(DIR*,constchar*);
函数功能:
读一个目录中的目录项
5、FRESULTf_readdir(DIR*,FILINFO*);
函数功能:
读取目录的内容
6、FRESULTf_stat(constchar*,FILINFO*);
函数功能:
获取文件的状态
7、FRESULTf_mountdrv();
函数功能:
初始化文件系统
8、FRESULTf_write(FIL*,constBYTE*,WORD,WORD*);
函数功能:
写文件
9、FRESULTf_sync(FIL*);
函数功能:
同步文件缓冲区的内容到磁盘中
10、FRESULTf_delete(constchar*);
函数功能:
删除一个文件或者目录
11、FRESULTf_mkdir(constchar*);
函数功能:
创建一个目录
这就是文件系统提供的全部功能,灵活地运用上述函数,就可以编写出复杂的应用程序。
4.2文件系统的主要数据结构
1)UCFS结构体
/*文件系统结构体,保存文件系统的有关信息*/
typedefstruct{
BYTEfs_type;//文件系统类型
BYTEfiles;//当前已打开的文件的数目
BYTEsects_clust;//每个簇的扇区数
BYTEn_fats;//FAT表的数目
WORDn_rootdir;//根目录数(在FAT32中为0)
BYTEwinflag;//标记文件是否被改动过,为1时要回写
BYTEpad1;//站位,字节对齐
DWORDsects_fat;//每个FAT表所占的扇区数
DWORDmax_clust;//总的簇数
DWORDfatbase;//FAT区的起始扇区
DWORDdirbase;//根目录区的起始扇区
DWORDdatabase;//数据区的起始扇区
DWORDwinsect;//当前缓冲区中存储的扇区号
BYTEwin[512];//单个扇区缓存
}UCFS;
UCFS结构体记录了文件系统的所有信息,有了这个结构体,就可以方便地访问文件系统的每一部分。
2)DIR结构体
//目录结构体,表示一个目录
typedefstruct{
DWORDsclust;//起始簇
DWORDclust;//当前簇
DWORDsect;//当前扇区
WORDindex;//当前索引
}DIR;
作为目录项的指针,既可以用于记录一个特定文件在目录中的位置,又可以用于记录在目录中当前目录项指针的位置(类似于文件指针)。
3)FIL结构体
//文件结构体,表示一个文件
typedefstruct{
DWORDfptr;//文件读写指针
DWORDfsize;//文件大小
DWORDorg_clust;//文件起始簇
DWORDcurr_clust;//当前簇(fsize=0时为0)
DWORDcurr_sect;//当前扇区
DWORDdir_sect;//此文件的目录项所在的扇区
BYTE*dir_ptr;//指向文件目录项的指针
BYTE*buffer;//文件读写缓冲区
BYTEflag;//文件状态标识
BYTEsect_clust;//当前簇中剩余的扇区数
}FIL;
记录普通文件(不是目录文件)的详细信息,比如文件对应的目录项位置,文件起始簇号,文件指针,文件大小等。
4)FILINFO结构体
//文件信息的结构体,也可以表示目录,用fattrib区分
typedefstruct_FILINFO{
DWORDfsize;//文件大小
WORDfdate;//文件修改日期
WORDftime;//文件修改时间
BYTEfattrib;//文件属性
charfname[13];//文件名(8.3格式)
}FILINFO;
5)win[512]数组
位于FATFS结构体中,作为目录项或者FAT分配表的读写缓冲区。
它不是某一个文件专有的缓冲区,而是整个文件系统的公共读写缓冲区。
6)buffer指针
buffer是一个指向512字节缓冲区的指针,位于FIL结构体中,也就相当于是FIL中有一个512字节缓冲区的成员。
此512字节的缓冲区,是一个文件的专有缓冲区。
用于当文件的读写没有按照512字节对齐的时候,作为磁盘与用户读写缓冲区之间的临时缓冲区。
4.3各个函数的详细实现
1)move_window
函数原型:
BOOLmove_window(DWORDsector)
函数功能:
win[]操作函数(DBR、FAT表、目录项)
<1>读取新的扇区内容到临时缓冲区win[]
<2>同步win[]中的内容到磁盘
注意:
<1>如果读取新的扇区号就是现在存储在win[]中的扇区号,就什么也不操作
<2>如果不同,则根据情况同步win[]到磁盘中,并且将新扇区中的内容读取到win[]中
<3>如果sector为0,则函数功能变为同步win[]到磁盘中,不会读取0扇区的内容到win[]
输入参数:
sector要读取扇区的扇区号
与其他函数的关系:
此函数被下列函数直接或间接调用
第一类:
操作FAT表
①get_cluster
②put_cluster
③remove_chain
④create_chain
第二类:
操作MBR、DBR
⑤check_fs
第三类:
操作目录项所在扇区(目录的数据空间)
⑥trace_path
程序的实现方法:
首先判断要读取的扇区号是否与当前缓存在win[]中的扇区号一致。
倘若一致,则无需执行任何操作。
倘若不一致,再判断缓存在win[]中的内容是否被修改过,如果修改过,就需要更新到磁盘,最后还要把新扇区中的内容加载到win[]中。
当传入参数0时,0与当前缓存在win[]的扇区号肯定不一样,所以一定会同步win[]内容到磁盘中。
2)f_mountdrv
函数原型:
FRESULTf_mountdrv()
函数功能:
初始化磁盘;初始化UcFs对象,记录物理磁盘的相关参数。
函数实现方法:
首先调用磁盘初始化函数,对磁盘进行初始化。
然后读取物理磁盘0号扇区的内容,判断是否是DBR扇区。
如果不是DBR扇区,那么肯定就是MBR扇区,再从MBR扇区中获取DBR扇区的地址,将DBR扇区的内容调取到win[]中。
接下来从win[]中,填充UCFS类型的系统对象,这样物理磁盘和文件系统的参数就被保存到了这个对象中。
以后,程序就可以从全局变量--UcFs类型的变量,访问文件系统的每一个区域。
3)f_open
函数原型:
FRESULTf_open(FIL*fp,constchar*path,BYTEmode)
函数功能:
以指定的方式打开或者新建一个文件。
如果打开或者创建成功,会填充fp指向的文件信息变量(包含文件的目录项确切位置和文件的信息)。
函数参数:
fp指向文件信息变量的指针
path指向文件的路径
mode打开方式
输出参数:
FR_OK打开或者创建成功
其他值打开或者创建失败
函数实现方法:
1以只读的方式打开一个已经存在的文件
首先调用函数trace_path搜索文件系统中是否存在目标文件,如果不存在就返回失败;如果存在就返回文件的目录项位置(dirscan、dir),并且将目录项所在扇区的内容加载到win[]中。
接下来就是从win[]中,将文件目录项的参数稍作转化后传入FIL类型的变量中。
到此,一个文件就算完整的打开了。
注意打开文件并不是打开文件的内容,而是文件的目录项,知道了文件的目录项就知道了如何去查看文件的内容。
以后,通过FIL类型的变量就可以操作对应的文件。
②新建一个文件
首先调用函数trace_path搜索文件系统中是否存在目标文件,因为是新建文件肯定不存在。
那么不存在的文件就返回新建文件当前文件夹的目录指针位置(dirscan、dir)--第一个空目录项所在位置,并且将当前目录指针所在扇区的内容加载到win[]中。
首先给新建文件在当前文件夹中预定一个目录项位置,然后填入新建文件的目录项初始值(文件名、扩展名、属性、创建时间、更新时间)到win[]中。
注意这里并不会将新建文件目录项所在扇区同步到磁盘中,只有当调用f_sync函数时才会将文件的目录项所在扇区同步到磁盘。
创建一个新文件,只会在其上一层目录中添加对应的目录项并初始化,并不会给文件分配数据空间,当然文件的大小肯定是0。
③重建一个文件
首先调用函数trace_path搜索文件系统中是否存在目标文件,因为是重建文件肯定存在。
那么就返回文件的目录项位置(dirscan、dir),并且将目录项所在扇区的内容加载到win[]中。
重建首先将文件的簇链删除,然后设置文件起始位置和文件大小为空,还需要初始化文件的属性、创建时间和修改时间。
这里的修改都只是在win[]中进行的,并没有同步到磁盘。
只有当调用f_sync函数时才会将文件的目录项所在扇区同步到磁盘。
重建文件更改了原来文件在目录中的目录项信息,重建文件并没有分配簇,也就是没有分配数据空间。
4)f_read
函数原型:
FRESULTf_read(FIL*fp,BYTE*buff,WORDbtr,WORD*br)
函数功能:
文件读操作
输入参数:
fp文件信息指针
buff指向用户缓冲区
btr准备读取的字节数
br指向实际读取字节数的变量
输出参数:
FRESULT成功与否
备注:
函数在读取文件内容后,还会移动文件指针到下一此读写操作的起点。
函数的实现方法:
读文件的情况有些复杂,不同的情况有不同的处理方法。
开始读的时候,文件指针并没有位于扇区边界上(512字节对齐),读取的跨度为3个簇。
首先读没有对齐扇区的剩余内容,其实这个内容在以前的函数(以前的函数移动了文件指针)已经将这个扇区的内容加载到了buffer中。
所以,直接从缓冲区buffer中读取此扇区文件指针以后的剩余内容到用户缓冲区。
接下来,读取第一个簇的剩余一个扇区的内容到用户缓冲区。
通过get_cluster函数从FAT表中,获取第二个簇链的位置。
然后一次性的将一个簇链的所有扇区内容读取到用户缓冲区中。
再通过get_cluster函数从FAT表中,获取第三个簇链的位置。
然后将第三个簇链的第一个扇区内容读取到用户缓冲区中。
最后,将最后所需要读取剩余内容所在的扇区(剩余部分不够一个扇区)读取到buffer中,然后再从buffer中读取所需要的剩余内容到用户缓冲区中。
到这里为止,整个读取操作已经完成。
由于buffer中还有一部分内容没读,假设继续调用函数f_read函数读取数据,那么肯定先从这个buffer缓冲区中将文件指针以后的扇区剩余内容读取到用户缓冲区。
5)f_write
函数原型:
FRESULTf_write(FIL*fp,constBYTE*buff,WORDbtw,WORD*bw)
函数功能:
文件写操作,只对文件的数据区进行写入,并没有更新对应的目录项。
输入参数:
fp文件信息指针
buff指向读取的用户缓冲区
btw准备写入的字节数
bw返回实际写入的字节数
输出参数:
FRESULT成功与否
备注:
函数在写完文件内容后,还会移动文件指针到下一此读写操作的起点。
函数的实现方法:
写文件的情况与读取文件内容类似。
开始写的时候,文件指针并没有位于扇区边界上(512字节对齐),写入数据的跨度为3个簇。
首先写入没有对齐扇区的剩余内容,其实这个内容在以前的函数(以前的函数移动了文件指针)已经将这个扇区的内容加载到了buffer中。
所以,将用户缓冲区中对应的内容写入到buffer中(从文件指针开始到buffer结束的这部分空间)。
然后再将buffer中的内容写入到磁盘对应的扇区。
接下来,将用户缓冲区写入到第一个簇的剩余一个扇区中。
通过creat_chain函数从FAT表中,获取第二个簇链的位置(如果是文件有剩余簇链则使用文件的剩余簇链,如果已经用完则重新从FAT表中搜索一个空的簇链连接到此文件中,也就是更改了文件的大小)。
然后一次性的将用户缓冲区写入到第二个簇链的所有扇区中。
再通过get_cluster函数从FAT表中,获取第三个簇链的位置。
然后将用户缓冲区写入到第三个簇链的第一个扇区中。
最后,将最后所需要写入剩余内容所在的扇区(剩余部分不够一个扇区)读取到buffer中,然后再将用户缓冲区中剩余内容写入到buffer中。
到这里为止,整个读取操作已经完成。
注意这里并没有将buffer的内容写入到磁盘中。
当调用f_sync函数的时候才会将buffer的内容同步到磁盘。
在函数返回之前,还需要判断文件大小是否更改了,如果大小更改了则要更新文件的大小,并将FA__WRITTEN记录到文件的flag中。
这样做的目的是为了当执行f_sync时,可