操作系统课程设计.docx
《操作系统课程设计.docx》由会员分享,可在线阅读,更多相关《操作系统课程设计.docx(32页珍藏版)》请在冰豆网上搜索。
操作系统课程设计
课程设计说明书
学院:
计算机科学与工程学院
专业:
计算机科学与技术
姓名:
杨天驹
学号:
0900310327
指导教师:
黄廷辉
2012年3月5日
操作系统课程设计报告
GeekOS操作系统的研究与实现
(项目0-------项目2)
一、实验目的:
熟悉GeekOS项目编译运行环境、核态进程的实现、用户态进程的实现、进程调度策略和算法实现、分页存储管理的实现和文件系统的实现等。
二、项目设计要求:
GeekOS设计项目0:
1.搭建GeekOS的编译和调试平台,掌握GeekOS的内核进程工作原理。
2.熟悉键盘操作函数,编程实现一个内核进程。
该进程的功能是:
接受键盘输入的字符并显示到屏幕上,当输入Ctrl+D时,结束进程的运行。
GeekOS设计项目1:
1.修改/geekos/elf.c文件:
在函数Parse_ELF_Executable()中添加代码,分析ELF格式的可执行文件(包括分析得出ELF文件头、程序头,获取可执行文件长度、代码段、数据段等信息),并填充Exe_Format数据结构中的域值。
2.掌握GeekOS在核心态运行用户程序的原理,为项目2的实现做准备。
GeekOS设计项目2:
本项目要求用户对以下几个文件进行修改:
1.src/GeekOS/user.c文件中的函数Spawn(),其功能是生成一个新的用户级进程。
2.src/GeekOS/usre.c文件中的函数Switch_To_User_Context(),调度程序在执行一个新的进程前调用该函数以切换用户地址空间。
3.src/GeekOS/elf.c文件中的函数Parse_ELF_Executable()。
该函数的实现要求和项目1相同。
4.src/GeekOS/userseg.c文件中主要是实现一些为实现对src/GeekOS/user.c中高层操作支持的函数。
(1)Destroy_User_Context()函数的功能是释放用户态进程占用的内存资源。
(2)Load_User_Program()函数的功能是通过加载可执行文件镜像创建新进程的User_Context结构。
(3)Copy_From_User()和Copy_To_User()函数的功能是在用户地址空间和内核地址空间之间复制函数,在分段存储器管理模式下,只要段有效,调用memcpy函数就可以实现这两个函数的功能。
(4)Switch_To_Address_Space()函数的功能是通过将进程的LDT装入到LDT寄存器来激活用户的地址空间。
5.src/GeekOS/kthread.c文件中的Start_User_Thread函数和Setup_User_Thread函数。
(1)Setup_User_Thread()函数的功能是为进程初始化内核堆栈,堆栈中是为进程首次进入用户态运行时设置处理器状态要使用的数据。
(2)Start_User_Thread()是一个高层操作,该函数使用User_Context对象开始一个新进程
6.src/GeekOS/Syscall.c文件中主要是实现用户程序要求内核进行服务的一些系统调用函数定义。
要求用户实现的有Sys_Exit()函数、Sys_PrintString()函数、
Sys_GetKey()、Sys_SetAttr()、Sys_Getcursor()、Sys_PutCursor()函数、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。
三、如何建立开发环境:
(一)利用linux安装盘安装了ubuntu10.10版本的linux操作系统环境;
(二)联网后通过系统里的更新管理器更新了系统,并安装了语言包和必要的驱动。
(三)在ubuntu软件中心下载安装了NASM汇编器、BochsPC模拟器以及bochs-x插件(保证ubuntu10.10环境下的bochs正常运行)。
四、项目设计原理:
Make工作原理:
在默认的方式下,只要输入make命令就可以工作。
具体的处理过程如下:
(1)make会在当前目录下找文件名为“Makefile”或“makefile”的文件。
(2)如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,它会找到“edit”这个文件,并把这个文件作为最终的目标文件。
(3)如果edit文件不存在,或是edit所依赖的后面的.o文件的修改时间要比edit这个文件新,那么,就会执行后面所定义的命令来生成edit这个文件。
(4)如果edit所依赖的.o文件也不存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件(这有点像一个堆栈的过程)。
(5)如果指定的C文件和H文件是存在的,make会生成.o文件,然后再用.o文件生成make的最终任务,也就是链接生成执行文件edit。
GeekOS的makefile文件功能:
(1)指定GeekOS如何被编译,哪些源文件被编译,哪些用户程序被编译等等。
通常不同项目的编译仅仅需要修改这一部分。
(2)定义了编译GeekOS要用到的工具程序。
(3)指定规则:
描述系统如何编译源程序。
(4)指定系统编译生成的指定文件。
GeekOS项目的开发流程:
1.开始一个GeekOS项目,第一步是添加相应的代码。
2.在Linux下利用make命令编译系统,生成系统镜像文件。
1$cd……/project0/build
2$makedepend
3$make
3.编写每个项目相应的Bochs的配置文件。
4.运行Bochs模拟器,执行GeekOS内核。
1$cd……/bochs
2$bochs
3运行后,屏幕上会有一些提示。
运行GeekOS选择Beginsimulation,如果GeekOS编译成功,并且bochs的配置也没问题,将会看到一个模拟VGA的文本窗口,Geekos就能运行程序输出相应信息
(每个环境具体运行的命令格式会有一些不同)
内核线程的建立流程:
用户态进程创建流程
五、项目设计的具体实现(程序代码):
GeekOS设计项目0:
Main.c文件:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*
*KernelCcodeentrypoint.
*Initializeskernelsubsystems,mountsfilesystems,
*andspawnsinitprocess.
*/
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));
Print("WelcometoGeekOS!
\n");
Set_Current_Attr(ATTRIB(BLACK,GRAY));
voidEchoCount()
{
Keycodekeycode;
intcount;
count=0;
while
(1)
{
if(Read_Key(&keycode))
{
if((keycode&0x4000)==0x4000)
{
if((Wait_For_Key()&0x00ff)=='d')
{
//Print("%c",Wait_For_Key());
Set_Current_Attr(ATTRIB(BLACK,RED));
Print("Ctrl+dIsEntered!
ProgramEnded!
");
Exit
(1);
}
}
elseif(!
(keycode&KEY_SPECIAL_FLAG)&&!
(keycode&KEY_RELEASE_FLAG))
{
keycode&=0xff;
count=count+1;
Set_Current_Attr(ATTRIB(BLACK,CYAN));
Print("%c",(keycode=='\r')?
'\n':
keycode);
if(keycode=='\r')
{
count=count-1;
Set_Current_Attr(ATTRIB(AMBER,BLUE));
Print("Thecounntsis%d",count);
Print("\n");
count=0;
}
}
}
}
}
structKernel_Thread*kerThd;
kerThd=Start_Kernel_Thread(&EchoCount,0,PRIORITY_NORMAL,false);
/*Nowthisthreadisdone.*/
Exit(0);
}
GeekOS设计项目1:
Elf.c文件:
#include
#include
#include
#include/*fordebugPrint()statements*/
#include
#include
#include
#include
/**
*FromthedataofanELFexecutable,determinehowitssegments
*needtobeloadedintomemory.
*@paramexeFileDatabuffercontainingtheexecutablefile
*@paramexeFileLengthlengthoftheexecutablefileinbytes
*@paramexeFormatstructuredescribingtheexecutable'ssegments
*andentryaddress;tobefilledin
*@return0ifsuccessful,<0onerror
*/
intParse_ELF_Executable(char*exeFileData,ulong_texeFileLength,
structExe_Format*exeFormat)
{
/*TODO("ParseanELFexecutableimage");*/
inti;
elfHeader*head=(elfHeader*)exeFileData;
programHeader*proHeader=(programHeader*)(exeFileData+head->phoff);
KASSERT(exeFileData!
=NULL);
KASSERT(exeFileLength>head->ehsize+head->phentsize*head->phnum);
KASSERT(head->entry%4==0);
exeFormat->numSegments=head->phnum;
exeFormat->entryAddr=head->entry;
for(i=0;iphnum;i++)
{
exeFormat->segmentList[i].offsetInFile=proHeader->offset;
exeFormat->segmentList[i].lengthInFile=proHeader->fileSize;
exeFormat->segmentList[i].startAddress=proHeader->vaddr;
exeFormat->segmentList[i].sizeInMemory=proHeader->memSize;
exeFormat->segmentList[i].protFlags=proHeader->flags;
proHeader++;
}
return0;
}
GeekOS设计项目2:
Src/GeekOS/user.c文件中的函数Spawn():
intSpawn(constchar*program,constchar*command,structKernel_Thread**pThread)
{
/*
*Hints:
*-CallRead_Fully()toloadtheentireexecutableintoamemorybuffer
*-CallParse_ELF_Executable()toverifythattheexecutableis
*valid,andtopopulateanExe_Formatdatastructuredescribing
*howtheexecutableshouldbeloaded
*-CallLoad_User_Program()tocreateaUser_Contextwiththeloaded
*program
*-CallStart_User_Thread()withthenewUser_Context
*
*Ifallgoeswell,storethepointertothenewthreadin
*pThreadandreturn0.Otherwise,returnanerrorcode.
*/
intrc;
char*exeFileData=0;
ulong_texeFileLength;
structUser_Context*userContext=0;
structKernel_Thread*process=0;
structExe_FormatexeFormat;
/*
*Loadtheexecutablefiledata,parseELFheaders,
*andloadcodeanddatasegmentsintousermemory.
*/
if((rc=Read_Fully(program,(void**)&exeFileData,&exeFileLength))!
=0||
(rc=Parse_ELF_Executable(exeFileData,exeFileLength,&exeFormat))!
=0||
(rc=Load_User_Program(exeFileData,exeFileLength,&exeFormat,command,&userContext))!
=0)
gotofail;
/*
*Userprogramhasbeenloaded,sowecanfreethe
*executablefiledatanow.
*/
Free(exeFileData);
exeFileData=0;
/*Starttheprocess!
*/
process=Start_User_Thread(userContext,false);
if(process!
=0){
KASSERT(process->refCount==2);
/*ReturnKernel_Threadpointer*/
*pThread=process;
}else
rc=ENOMEM;
returnrc;
fail:
if(exeFileData!
=0)
Free(exeFileData);
if(userContext!
=0)
Destroy_User_Context(userContext);
returnrc;
}
Src/GeekOS/user.c文件中的函数Switch_To_User_Context():
voidSwitch_To_User_Context(structKernel_Thread*kthread,structInterrupt_State*state)
{
/*
*Hint:
Beforeexecutinginusermode,youwillneedtocall
*theSet_Kernel_Stack_Pointer()andSwitch_To_Address_Space()
*functions.
*/
staticstructUser_Context*s_currentUserContext;/*lastusercontextused*/
structUser_Context*userContext=kthread->userContext;
/*
*FIXME:
couldavoidresettingss0/esp0ifnotreturning
*touserspace.
*/
KASSERT(!
Interrupts_Enabled());
if(userContext==0){
/*Kernelmodethread:
noneedtoswitchaddressspace.*/
return;
}
/*Switchonlyiftheusercontextisindeeddifferent*/
if(userContext!
=s_currentUserContext){
ulong_tesp0;
/*Switchtoaddressspaceofusercontext*/
Switch_To_Address_Space(userContext);
/*
*Bydefinition,whenreturningtousermodethereisno
*contextremainingonthekernelstack.
*/
esp0=((ulong_t)kthread->stackPage)+PAGE_SIZE;
/*Changetothekernelstackofthenewprocess.*/
Set_Kernel_Stack_Pointer(esp0);
/*Newusercontextisactive*/
s_currentUserContext=userContext;
}
}
src/GeekOS/elf.c文件中的函数Parse_ELF_Executable():
intParse_ELF_Executable(char*exeFileData,ulong_texeFileLength,
structExe_Format*exeFormat)
{
elfHeader*hdr;
programHeader*phdr;
inti;
hdr=(elfHeader*)exeFileData;
/*
*FIXME:
whencheckingoffsets,wereallyoughttobe
*checkingoverflowcases.Needtousefunctionsfrom
*range.h(whichneedstobeimplemented,too)
*/
if(exeFileLengthstrncmp(exeFileData,"\x7F""ELF",4)!
=0){
if(elfDebug)Print("NotanELFexecutable\n");
returnENOEXEC;
}
if(hdr->phnum>EXE_MAX_SEGMENTS){
if(elfDebug)Print("Toomanysegments(%d)inELFexecutable\n",hdr->phnum);
returnENOEXEC;
}
if(exeFileLengthphoff+(hdr->phnum*sizeof(programHeader))){
if(elfDebug)Print("Notenoughroomforprogramheader\n");
returnENOEXEC;
}
exeFormat->numSegments=hdr->phnum;
exeFormat