计算机系统实验软件报告范文文档格式.docx

上传人:b****6 文档编号:20530001 上传时间:2023-01-23 格式:DOCX 页数:22 大小:392.39KB
下载 相关 举报
计算机系统实验软件报告范文文档格式.docx_第1页
第1页 / 共22页
计算机系统实验软件报告范文文档格式.docx_第2页
第2页 / 共22页
计算机系统实验软件报告范文文档格式.docx_第3页
第3页 / 共22页
计算机系统实验软件报告范文文档格式.docx_第4页
第4页 / 共22页
计算机系统实验软件报告范文文档格式.docx_第5页
第5页 / 共22页
点击查看更多>>
下载资源
资源描述

计算机系统实验软件报告范文文档格式.docx

《计算机系统实验软件报告范文文档格式.docx》由会员分享,可在线阅读,更多相关《计算机系统实验软件报告范文文档格式.docx(22页珍藏版)》请在冰豆网上搜索。

计算机系统实验软件报告范文文档格式.docx

它尽量的简化了系统,但却提供了现代操作系统必须的最基本的功能,例如,虚拟内存,一个文件系统和交互进程通信。

GeekOS主要用于操作系统课程设计,目的是使学生能够实际动手参与到一个操作系统的开发工作中.出于教学目的,这个系统内核设计简单,却又兼备实用性,它可以运行在真正的X86PC硬件平台.作为一个课程设计平台,GeekOS由一个基本的操作系统内核作为基础,提供了操作系统与硬件之间的所有必备接口,实现了系统引导,实模式到保护模式的转换,中断调用及异常处理,基于段式的内存管理,FIFO进程调度算法以及内核进程,基本的输入输出(键盘作为输入设备,显示器作为输出设备),以及一个用于存放用户程序的只读文件系统PFAT。

由于目前大家最通用的处理器是CISC结构的IntelIA-32或X86通用处理器,所以选用该结构的教学操作系统比较合适,GeekOS作为一个用C语言开发的操作系统,学生可以在Linux或Unix环境下对其进行功能扩充,也可以在Windows下使用Cygwin工具开发,且其针对进程、文件系统、存储管理等操作系统核心内容分别设计了7个难度逐渐增加的项目供学生选择。

2.实验环境

1)安装VMwareWorkstation7.1.3

2)建立虚拟机,在虚拟机上安装Redhat9.0

3)安装VMwareTools

4)实现文件共享,添加桌面上名为ZJ的文件夹为共享文件,在linux下/mnt/hgfs/zj可找到

5)Bochs-2.2.1的安装和使用

Bochs是一个x86硬件平台的模拟器。

换句话说,它可以模拟各种硬件的配置。

当启动到Bochs时,看起来就好像你在自己的PC上启动了另外一个PC。

Bochs模拟的是整个PC平台,包括I/O设备、内存和BIOS。

更为有趣的是,甚至可以不使用PC硬件来运行Bochs。

事实上,它可以在任何编译运行Bochs的平台上模拟x86硬件。

通过改变配置,可以指定使用的CPU(386、486或者586),以及内存大小等。

一句话,Bochs是电脑里的“PC”。

根据需要,Bochs还可以模拟多台PC,此外,它甚至还有自己的电源按钮。

注释行用#开头.对Bochs的手动配置主要通过Bochsrc.txt文件来实现。

可通过该文件的修改来规定启动方式(软盘、硬盘),用来模拟软盘或硬盘的映像文件等。

3.实验目的

通过分析、编译GeekOS系统的源代码并生成磁盘映射文件,来进一步了解操作系统的原理。

并在Bochs模拟器下模拟启动,来了解操作系统的基本原理和系统的启动过程。

项目0:

熟悉Geekos的项目编译、调试和运行环境,掌握Geekos运行工作过程。

项目1:

熟悉ELF文件格式,了解Geekos系统如何将ELF格式的用户可执行程序装入到内存,建立内核进程并运行的实现技术。

项目2:

扩充GeekOS操作系统内核,使得系统能够支持用户级进程的动态创建和执行。

4.项目设计要求

4.1项目0的要求

(1)搭建GeekOS的编译和调试平台,掌握GeekOS的内核进程工作原理。

(2)熟悉键盘操作函数,编程实现一个内核进程。

该进程的功能是:

接收键盘输入的字符并显示到屏幕上,当输入Ctrl+D时,结束进程的运行。

4.2项目1的要求

(1)修改/geekos/elf.c文件:

在函数Parse_ELF_Executable()中添加代码,分析ELF格式的可执行文件(包括分析得出ELF文件头、程序头,获取可执行文件长度,代码段、数据段等信息),并填充Exe_Format数据结构中的域值。

(2)掌握GeekOS在核心态运行用户程序的原理,为项目2的实现做准备。

4.3项目2的要求

开始本项目前需要阅读/src/geekos目录中的以下程序:

(1)Entry.c:

用户程序外壳,用户程序的入口地址就在这里,此文件在编译时与用户程序一起编译。

(2)Lowlevel.asm:

其中HandleInterrupt是中断处理的总调度程序,该函数根据传递的中断向量查找并调用相关的中断处理程序,并实现调度进程的选择。

Switch_To_Thread函数用于实现进程的切换。

(3)Kthread.c:

内核进程有关函数以及进程调度算法都在此实现。

(4)Userseg.c:

其中要关注的函数有

Destroy_User_Context()函数功能是释放User_Context空间,Detach_User_Context()调用该函数。

Load_User_Program()函数功能是对用户进程的User_Context结构初始化,并对用户态进程的初始化,Spawn()函数中调用该函数。

项目2要求用户对以下几个文件进行修改:

(1)“src/GeekOS/user.c”文件中的函数Spawn(),其功能是生成一个新的用户级进程。

(2)“src/GeekOS/user.c”文件中的函数Switch_To_User_Context(),调度程序在执行一个新的进程前调用该函数以切换用户地址空间。

(3)“src/GeekOS/elf.c”文件中的函数Parse_ELF_Executable(),该函数的实现要求和项目1相同。

(4)“src/GeekOS/userseg.c”文件中主要是实现一些为实现“src/GeekOS/

user.c”中高层操作支持的函数。

Destroy_User_Context()函数的功能是释放用户态进程占用的内存资源。

Load_User_Program()函数的功能通过加载可执行文件镜像创建新进程的User_Context结构。

Copy_From_User()和Copy_To_User()函数的功能是在用户地址空间和内核地址空间之间复制数据,在分段存储器管理模式下,只要段有效,调用memcpy函数就可以实现这两个函数的功能。

Switch_To_Address_Space()函数的功能是通过将进程的LDT装入到LDT寄存器来激活用户的地址空间。

(5)“src/GeekOS/kthread.c”文件中的Start_User_Thread函数和Setup_User_

Thread函数。

Setup_User_Thread()函数的功能是为进程初始化内核堆栈,堆栈中是为进程首次进入用户态运行时设置处理器状态要使用的数据。

Start_User_Thread()是一个高层操作,该函数使用User_Context对象开始一个新进程。

(6)“src/GeekOS/kthread.c”文件中主要是实现用户程序要求内核进行服务的一些系统调用函数定义。

要求用户实现的有Sys_Exit()函数、Sys_PrintString()函数、Sys_GetKey()、Sys_SetAttr()、Sys_GetCursor()、Sys_PutCursor()、Sys_Spawn()函数、Sys_Wait()函数和Sys_GetPID()函数。

最后,需要在main.c文件中改写生成第一个用户态进程的函数调用:

Spawn_Init_Process(void)。

需要注意的是:

作为与用户沟通的界面,GeekOS提供了一个简单的shell,保存在PFAT文件系统内,所以GeekOS系统启动后,应启动shell程序/c/shell.exe运行,所以需要将/c/shell.exe作为可执行文件传递给Spawn函数的program参数,创建第一个用户态进程,然后由它来创建其他进程。

添加代码运行成功后,GeekOS就可以挂载shell,并能运行测试文件c.exe和b.exe。

5.项目设计原理

5.1项目0的原理

项目0的实现主要由以下步骤完成(/mnt/hgfs/zj/project0/src/geekos/main.c中完成):

1.编写一个C语言函数,函数功能是:

接收键盘输入的按键,并将键值在显示器显示出来,当输入ctrl+d就退出;

2.在Main函数体内调用Start_Kernel_Thread函数,将步骤1编写的函数地址传递给参数startFunc,利用Setup_Kernel_Thread函数建立一个待运行的线程。

3.在Linux环境下编译系统得到GeekOS镜像文件。

4.编写一个相应的bochs配置文件。

5.在bochs中运行GeekOS系统显示结果。

5.2项目1的原理

ELF文件格式如图4-1:

图4-1

文件最开始是ELF文件头结构,跟着是ProgramesHeader表,接下来是各个区段,最后是可选的区段头表。

ELF文件是x86Linux系统下的一种常用目标文件(objectfile)格式,有三种主要类型:

(1)适于连接的可重定位文件(relocatablefile),可与其它目标文件一起创建可执行文件和共享目标文件。

(2)适于执行的可执行文件(executablefile),用于提供程序的进程映像,加载的内存执行。

(3)共享目标文件(sharedobjectfile),连接器可将它与其它可重定位文件和共享目标文件连接成其它的目标文件,动态连接器又可将它与可执行文件和其它共享目标文件结合起来创建一个进程映像。

为了方便和高效,ELF文件内容有两个平行的视图:

一个是程序连接角度,另一个是程序运行角度。

GeekOS中的用户程序全部在系统的编译阶段完成编译和连接,形成可执行文件,用户可执行文件保存在PFAT文件系统中。

内存中的可执行文件镜像如图4-2:

图4-2

内核线程的建立流程如图4-3:

图4-3

本项目要完成的就是在系统启动后,从PFAT文件系统将可执行文件装入内存,建立进程并运行得到相应的输出。

此过程主要由Spawner函数实现,先调用Read_Fully函数将文件读入内存,后调用Parse_ELF_Executable函数分析ELF文件,最后调用Spawn_Program函数将可执行程序的代码段和数据段等装入内存,此后便可以开始运行一个内核级进程了。

在本项目中,我们要完成Parse_ELF_Executable函数,此函数的作用为根据ELF文件格式,从exeFileData指向的内容中得到ELF文件头,继续分析可得到程序头和程序代码段等信息。

5.3项目2的原理

进程是可并发执行的程序在某个数据集合上的一次计算活动,也是操作系统资源分配和保护的基本单位。

进程和程序有着本质的区别,程序是一些能保存在磁盘上的指令的有序集合,没有任何执行的概念;

而进程是程序执行的过程,包括了创建、调度和消亡的整个过程。

因此,对系统而言,当用户在系统中输入命令执行一个程序时,它将启动一个进程。

在GeekOS中,进程的执行过程分为运行态、就绪态和等待态。

GeekOS为不同状态的进程准备了不同的进程队列(Thread_Queue)。

如果一个进程正处于就绪态,就会在队列s_runQueue中出现;

如果一个进程处于等待态,就会在s_reaperWaitQueue队列中出现;

如果一个进程准备被销毁,就会在s_graveyardQueue队列中出现。

由于处于运行态的进程最多只能有一个,所以没有队列,由指针g_currentThread指向此进程。

系统中每个进程有且仅有一个进程控制块(PCB),它记录了有关进程的所有信息,GeekOS的PCB用数据结构Kernel_Thread来表示。

GeekOS最早创建的内核级进程是Idle、Reaper和Main。

GeekOS在几种情况下会进行进程切换:

一是时间片用完时;

二是执行进程Idle时;

三是进程退出调用Exit函数时;

四是进程进入等待态调用Wait函数时。

用户进程切换通过Switch_To_User_Context函数实现,此函数负责检测当前进程是否为用户级进程,若是就切换至用户进程空间,它由我们自己实现。

在GeekOS中为了区分用户级进程与内核级进程,在Kernel_Thread结构体中设置了一个字段userContext,它指向用户态进程上下文。

对于内核级进程来说,此指针为空。

因此,要判断一个进程是用户级的还是内核级的,只要判断userContext字段是否为空就行了。

新建和注销User_Context结构的函数分别是Create_User_Context函数和Destroy_User_Context函数,它们都由我们自己实现。

每个用户态进程都要占用一段物理上连续的内存空间,存储用户级进程的数据和代码。

所以,为了实现存取访问控制,每个用户级进程都有属于自己的内存段空间,每一个段有一个段描述符,并且每一个进程都有一个段描述符表(LDT),它用于保存此进程的所有段描述符。

为用户级进程创建LDT的步骤是:

(1)调用Allocate_Segment_Descriptor()新建一个LDT描述符;

(2)调用Selector()新建一个LDT选择子;

(3)调用Init_Code_Segment_Descriptor()新建一个文本段描述符;

(4)调用Init_Data_Segment_Descriptor()新建一个数据段描述符;

(5)调用Selector()新建一个数据段选择子;

(6)调用Selector()新建一个文本段选择子。

从以上描述可知,用户态进程除了需要数据段和代码段用于装入可执行文件和数据外,还需要另外两个数据结构的存储空间,一个是进程的堆栈,另一个是参数块数据结构。

在用户态进程首次被调度前,系统必须初始化用户态进程的堆栈,使之看上去像进程刚被中断运行一样,因此需要使用Push函数将以下数据压入堆栈:

数据选择子、堆栈指针、Eflags、文本选择子、程序计数器、错误代码、中断号、通用寄存器、DS寄存器、ES寄存器、FS寄存器和GS寄存器。

GeekOS系统初始内核不支持用户态进程,因此我们必须创建用户态进程。

GeekOS的用户态进程创建过程可以描述如下:

(1)Spawn函数导入用户程序并初始化:

调用Load_User_Program进行User_Context的初始化及用户态进程空间的分配及用户程序各段的装入;

(2)Spawn函数调用Start_User_Thread(),初始化一个用户态进程,包括初始化进程Kernel_Thread结构以及调用Setup_User_Thread初始化用户态进程的内核堆栈;

Set_User_Thread内部需要调用Attach_User_Context加载用户上下文。

(3)最后Spawn函数退出,这时用户级进程已被添加至系统运行进程队列,可以被调度了。

6.项目设计的具体实现

6.1Project0的实现:

首先需要了解一下将要用到的二个函数:

Read_Key()、Start_Kernel_Thread()。

Read_Key是键盘相关的函数,在keyboard.h中定义,project0的main.c文件中已经包含有了此头文件。

1)Read_Key()的介绍:

定义:

boolRead_Key(Keycode*keycode);

作用:

轮查键盘事,如果捕获到键盘事件,则返回true,并且将按键码保存到参数keycode地址中。

函数返回数据类型都是16位的Keycode类型,定义为typedefushort_tKeycode,在keyboard.h中还定义了一系列的键盘码常量。

Keycode的高8位为标志位。

如果第8位为0,则keycode的低8位为按键的ASCII码,如果第8位为1,表示按下的是F1-12系列或其ctrl\shift\alt等特殊键。

其它常用的几个标志位:

第12位:

SHIFT_FLAG,为1表示shift键处于按下状态

第13位:

ALT_FLAG,为1表示alt键处于按下状态

第14位:

CTRL_FLAG,为1表示Ctrl键处于按下状态

第15位:

RELEASE_FLAG,为1是释放事件,为0是按下事件,就是windows编程中常用的keyup和keydown。

2)Start_Kernel_Thread()的介绍:

voidInit_Scheduler(void);

structKernel_Thread*Start_Kernel_Thread(

Thread_Start_FuncstartFunc, 

//线程函数地址

ulong_targ,//线程函数参数

intpriority,//优先级

booldetached 

//线程属性,false为内核线程,true为用户线程

);

创建并启动一个线程。

此函数返回的是Kernel_Thread结构体类型数据,在这里我们不对此结构体作深入介绍。

具体编码:

voidkeyboard(ulong_targ)

{

Keycodekey;

Print("

PleaseEnterthecharacters.press'

Ctrl+d'

toexit\n\n"

while(true)

{

Set_Current_Attr(ATTRIB(BLACK,RED|BRIGHT));

key=Wait_For_Key();

if(key==(KEY_CTRL_FLAG+'

d'

))

\n\nKeyEnd\n"

break;

}

if(!

(key&

KEY_RELEASE_FLAG)&

&

!

KEY_SPECIAL_FLAG))

%c"

key);

}

voidMain(structBoot_Info*bootInfo)

Init_BSS();

Init_Screen();

Init_Mem(bootInfo);

Init_CRC32();

Init_TSS();

Init_Interrupts();

Init_Scheduler();

Init_Traps();

Init_Timer();

Init_Keyboard();

Set_Current_Attr(ATTRIB(BLACK,GREEN|BRIGHT));

\n\nWelcometosutingting'

sGeekOS!

\n\n"

Start_Kernel_Thread(keyboard,0,5,1);

Set_Current_Attr(ATTRIB(BLACK,GRAY));

TODO("

Startakernelthreadtoechopressedkeysandprintcounts"

Exit(0);

调试运行结果:

新建终端终端,进入/mnt/hgfs/zj/project0/build目录,执行makedepend及make命令,此时会在build目录下生成fd.img和diskc.img,接下来执行bochs命令,运行后,屏幕上会有一些提示,运行GeekOS选择Beginsimulation,成功后将会看到一个模拟VGA的文本窗口,GeekOS就能运行程序输出相应信息。

如果GeekOS没有正确运行,查阅在build目录中的bochs.out文件(运行后产生),如果是错误使得Bochs不能运行,或者GeekOS系统崩溃,bochs.out文件中都将会包含相应的诊断信息。

对于Project0,在运行之后,我们在键盘上输入常规的字符,则会在屏幕上进行显示,若我们按Ctrl+d,则停止读键盘,并打印出提示ThreadEnd,程序运行参考截图如图5-1所示:

图5-1

6.2Project1的实现:

在此项目中,GeekOS需要从磁盘加载一个可执行文件到内存中,并且在内核线程中执行此程序。

我们需要做的就是完成/geekos/elf.c中Parse_ELF_Executable函数,把EXE文件的内容填充到指定的Exe_format格式的区域。

/geekos/elf.c中Parse_ELF_Executable函数的定义如下:

intParse_ELF_Executable(char*exeFileData,ulong_texeFileLength,structExe_Format*exeFormat)

exeFileData为ELF(ExecutableandLinkingFormat)格式的可执行文件的内容,exeFileLength为文件的长度,函数就是需要把exeFileData中的数据填充到exeFormat中。

在函数Prase_ELF_Executable()中添加代码:

intParse_ELF_Executable(char*exeFileData,ulong_texeFileLength, 

structExe_Format*exeFormat)

{inti;

elfHeader*hdr=(elfHeader*)exeFileData;

programHeader*phdr=(programHeader*)(exeFileData+hdr->

phoff);

structExe_Segment*segment=exeFormat->

segmentList;

for(i=0;

i<

hdr->

phnum;

i++)

segment->

offsetInFile=phdr->

offset;

segment->

lengthInFile=phdr->

fileSize;

startAddress=phdr->

vaddr;

sizeInMemory=phdr->

memSize;

phdr++;

segment++;

exeFormat->

numSegments=hdr->

entryAddr=hdr->

entry;

return0;

}

在Linux终端中,输入cd/mnt/hgfs/os/project1/build进入项目目录,然后输入makedepend及make命令,接下来输入bochs命令,运行后,屏幕上会有一些提示,运行GeekOS选择Beginsimulation,如果GeekOS编译成功,并且Bochs的配置也没问题,将会看到一个模拟VGA的文本窗口,GeekOS就能运行程序输出相应信息。

如果GeekOS没有正确运行,查阅在build目录中的bochs.out文件(运行后产生)

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 理学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1