1、 课程设计阐明书学 院: 计算机科学与工程学院 专 业: 计算机科学与技术 姓 名: 杨天驹 学 号: 指引教师: 黄廷辉 3 月 5 日操作系统课程设计报告GeekOS操作系统研究与实现(项目0-项目2)一、 实验目:熟悉GeekOS项目编译运营环境、核态进程实现、顾客态进程实现、进程调度方略和算法实现、分页存储管理实现和文献系统实现等。二、 项目设计规定:GeekOS设计项目0:1. 搭建GeekOS编译和调试平台,掌握GeekOS内核进程工作原理。2. 熟悉键盘操作函数,编程实现一种内核进程。该进程功能是:接受键盘输入字符并显示到屏幕上,当输入Ctrl+D时,结束进程运营。GeekOS设
2、计项目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、新进程前调用该函数以切换顾客地址空间。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()函数功能是在顾客地址空间和内核地址空
4、间之间复制函数,在分段存储器管理模式下,只要段有效,调用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对象开始一种新
5、进程 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提供了
6、一种简朴Shell,保存在PFAT文献系统内,因此GeekOS系统启动后,应启动shell程序/c/shell.exe运营,因此需要将/c/shell.exe作为可执行文献传递给Spawn函数program参数,创立第一种顾客态进程,然后由它来创立其她进程。 添加代码运营成功后,GeekOS就可以挂载shell,并能运营测试文献c.exe和b.exe。三、 如何建立开发环境:(一) 运用linux安装盘安装了ubuntu10.10版本linux操作系统环境;(二) 联网后通过系统里更新管理器更新了系统,并安装了语言包和必要驱动。(三) 在ubuntu软件中心下载安装了NASM汇编器、Bochs
7、 PC模仿器以及bochs-x插件(保证ubuntu10.10环境下bochs正常运营)。四、 项目设计原理:Make工作原理:在默认方式下,只要输入make命令就可以工作。详细解决过程如下:(1)make会在当前目录下找文献名为“Makefile”或“makefile”文献。 (2)如果找到,它会找文献中第一种目的文献(target),在上面例子中,它会找到“edit”这个文献,并把这个文献作为最后目的文献。 (3)如果edit文献不存在,或是edit所依赖背面.o文献修改时间要比edit这个文献新,那么,就会执行背面所定义命令来生成edit这个文献。 (4)如果edit所依赖.o文献也不存
8、在,那么make会在当前文献中找目的为.o文献依赖性,如果找到则再依照那一种规则生成.o文献(这有点像一种堆栈过程)。 (5)如果指定C文献和H文献是存在, make会生成.o文献,然后再用.o文献生成make最后任务,也就是链接生成执行文献edit。 GeekOSmakefile文献功能:(1) 指定GeekOS如何被编译,哪些源文献被编译,哪些顾客程序被编译等等。普通不同项目编译仅仅需要修改这一某些。(2) 定义了编译GeekOS要用到工具程序。(3) 指定规则:描述系统如何编译源程序。(4) 指定系统编译生成指定文献 。GeekOS项目开发流程:1. 开始一种GeekOS项目,第一步是添
9、加相应代码 。2. 在Linux下运用make命令编译系统,生成系统镜像文献。 $ cd /project0/build $ make depend $ make 3. 编写每个项目相应Bochs配备文献。4. 运营Bochs模仿器,执行GeekOS内核。 $ cd /bochs $ bochs 运营后,屏幕上会有某些提示。运营GeekOS选取Begin simulation,如果GeekOS 编译成功,并且bochs配备也没问题,将会看到一种模仿VGA文本窗口,Geekos就能运营程序输出相应信息 (每个环境详细运营命令格式会有某些不同)内核线程建立流程: 顾客态进程创立流程 五、 项目设计
10、详细实现(程序代码):GeekOS设计项目0:Main.c文献:#include #include #include #include #include #include #include #include #include #include #include /* * Kernel C code entry point. * Initializes kernel subsystems,mounts filesystems, * and spawns init process. */void Main(struct Boot_Info* bootInfo) Init_BSS(); Init_S
11、creen(); 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(Welcome to GeekOS!n); Set_Current_Attr(ATTRIB(BLACK,GRAY); void EchoCount() Keycode keycode; int count; count=0;
12、 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+d Is Entered!Program Ended!); Exit(1); else if ( !(keycode & KEY_SPECIAL_FLAG) & !(keycode & KEY_RELEASE_FLAG) ) keycode &=
13、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(The counnts is %d ,count); Print(n); count=0; struct Kernel_Thread *kerThd;kerThd = Start_Kernel_Thread(&EchoCount,0 ,PRIORITY_NORM
14、AL,false); /* Now this thread is done. */ Exit(0);GeekOS设计项目1:Elf.c文献:#include #include #include #include /* for debug Print() statements */#include #include #include #include /* * From the data of an ELF executable,determine how its segments * need to be loaded into memory. * param exeFileData buff
15、er containing the executable file * param exeFileLength length of the executable file in bytes * param exeFormat structure describing the executables segments * and entry address;to be filled in * return 0 if successful,phoff); KASSERT(exeFileData!=NULL); KASSERT(exeFileLengthhead-ehsize+head-phents
16、ize*head-phnum); KASSERT(head-entry%4=0); exeFormat-numSegments=head-phnum; exeFormat-entryAddr=head-entry; for(i=0;iphnum;i+) exeFormat-segmentListi.offsetInFile=proHeader-offset; exeFormat-segmentListi.lengthInFile=proHeader-fileSize; exeFormat-segmentListi.startAddress=proHeader-vaddr; exeFormat-
17、segmentListi.sizeInMemory=proHeader-memSize; exeFormat-segmentListi.protFlags=proHeader-flags; proHeader+;return 0;GeekOS设计项目2:Src/GeekOS/user.c文献中函数Spawn():int Spawn(const char *program,const char *command,struct Kernel_Thread *pThread) /* * Hints: * - Call Read_Fully() to load the entire executabl
18、e into a memory buffer * - Call Parse_ELF_Executable() to verify that the executable is * valid,and to populate an Exe_Format data structure describing * how the executable should be loaded * - Call Load_User_Program() to create a User_Context with the loaded * program * - Call Start_User_Thread() w
19、ith the new User_Context * * If all goes well,store the pointer to the new thread in * pThread and return 0. Otherwise,return an error code. */ int rc; char *exeFileData = 0; ulong_t exeFileLength; struct User_Context *userContext = 0; struct Kernel_Thread *process = 0; struct Exe_Format exeFormat;
20、/* * Load the executable file data,parse ELF headers, * and load code and data segments into user memory. */ if (rc = Read_Fully(program,(void*) &exeFileData,&exeFileLength) != 0 | (rc = Parse_ELF_Executable(exeFileData,exeFileLength,&exeFormat) != 0 | (rc = Load_User_Program(exeFileData,exeFileLeng
21、th,&exeFormat,command,&userContext) != 0) goto fail; /* * User program has been loaded,so we can free the * executable file data now. */ Free(exeFileData); exeFileData = 0; /* Start the process!*/ process = Start_User_Thread(userContext,false); if (process != 0) KASSERT(process-refCount = 2); /* Ret
22、urn Kernel_Thread pointer */ *pThread = process; else rc = ENOMEM; return rc;fail: if (exeFileData != 0) Free(exeFileData); if (userContext != 0) Destroy_User_Context(userContext); return rc;Src/GeekOS/user.c文献中函数Switch_To_User_Context():void Switch_To_User_Context(struct Kernel_Thread* kthread,stru
23、ct Interrupt_State* state) /* * Hint:Before executing in user mode,you will need to call * the Set_Kernel_Stack_Pointer() and Switch_To_Address_Space() * functions. */ static struct User_Context* s_currentUserContext; /* last user context used */ struct User_Context* userContext = kthread-userContex
24、t; /* * FIXME:could avoid resetting ss0/esp0 if not returning * to user space. */ KASSERT(!Interrupts_Enabled(); if (userContext = 0) /* Kernel mode thread:no need to switch address space. */ return; /* Switch only if the user context is indeed different */ if (userContext != s_currentUserContext) u
25、long_t esp0; /* Switch to address space of user context */ Switch_To_Address_Space(userContext); /* * By definition,when returning to user mode there is no * context remaining on the kernel stack. */ esp0 = (ulong_t) kthread-stackPage) + PAGE_SIZE; /* Change to the kernel stack of the new process. *
26、/ Set_Kernel_Stack_Pointer(esp0); /* New user context is active */ s_currentUserContext = userContext; src/GeekOS/elf.c文献中函数Parse_ELF_Executable():int Parse_ELF_Executable(char *exeFileData,ulong_t exeFileLength, struct Exe_Format *exeFormat) elfHeader *hdr; programHeader *phdr; int i; hdr = (elfHea
27、der *) exeFileData; /* * FIXME:when checking offsets,we really ought to be * checking overflow cases. Need to use functions from * range.h (which needs to be implemented,too) */ if (exeFileLength phnum EXE_MAX_SEGMENTS) if (elfDebug) Print(Too many segments (%d) in ELF executablen,hdr-phnum); return
28、 ENOEXEC; if (exeFileLength phoff + (hdr-phnum * sizeof(programHeader) if (elfDebug) Print(Not enough room for program headern); return ENOEXEC; exeFormat-numSegments = hdr-phnum; exeFormat-entryAddr = hdr-entry; phdr = (programHeader *) (exeFileData + hdr-phoff); for (i = 0;i phnum;+i) struct Exe_S
29、egment *segment = &exeFormat-segmentListi; /* * Fill in segment offset,length,address * FIXME:should check that segments are valid */ segment-offsetInFile = phdri.offset; segment-lengthInFile = phdri.fileSize; segment-startAddress = phdri.vaddr; segment-sizeInMemory = phdri.memSize; if (segment-leng
30、thInFile segment-sizeInMemory) if (elfDebug) Print(Segment %d:length in file (%lu) exceeds size in memory (%lu)n, i,segment-lengthInFile,segment-sizeInMemory); return ENOEXEC; /* Groovy */ return 0;src/GeekOS/userseg.c文献中函数Destroy_User_Context()void Destroy_User_Context(struct User_Context* userCont
31、ext) /* * Hints: * - you need to free the memory allocated for the user process * - dont forget to free the segment descriptor allocated * for the processs LDT */ / TODO(Destroy a User_Context); /* Free the contexts LDT descriptor */ Free_Segment_Descriptor(userContext-ldtDescriptor);userContext-ldt
32、Descriptor=0; /* Free the contexts memory */ Free(userContext-memory);userContext-memory=0; Free(userContext);userContext=0;src/GeekOS/userseg.c文献中函数Load_User_Program()int Load_User_Program(char *exeFileData,ulong_t exeFileLength, struct Exe_Format *exeFormat,const char *command, struct User_Context
33、 *pUserContext) /* * Hints: * - Determine where in memory each executable segment will be placed * - Determine size of argument block and where it memory it will * be placed * - Copy each executable segment into memory * - Format argument block in memory * - In the created User_Context object,set co
34、de entry point * address,argument block address,and initial kernel stack pointer * address */ int i; ulong_t maxva = 0; unsigned numArgs; ulong_t argBlockSize; ulong_t size,argBlockAddr; struct User_Context *userContext = 0; /* Find maximum virtual address */ for (i = 0;i numSegments;+i) struct Exe_
35、Segment *segment = &exeFormat-segmentListi; ulong_t topva = segment-startAddress + segment-sizeInMemory; /* FIXME:range check */ if (topva maxva) maxva = topva; /* Determine size required for argument block */ Get_Argument_Block_Size(command,&numArgs,&argBlockSize); /* * Now we can determine the siz
36、e of the memory block needed * to run the process. */ size = Round_Up_To_Page(maxva) + DEFAULT_USER_STACK_SIZE; argBlockAddr = size; size += argBlockSize; /* Create User_Context */ userContext = Create_User_Context(size); if (userContext = 0) return -1; /* Load segment data into memory */ for (i = 0
37、;i numSegments;+i) struct Exe_Segment *segment = &exeFormat-segmentListi; memcpy(userContext-memory + segment-startAddress, exeFileData + segment-offsetInFile, segment-lengthInFile); /* Format argument block */ Format_Argument_Block(userContext-memory + argBlockAddr,numArgs,argBlockAddr,command); /* Fill in code entry point */ userContext-entryAddr = exeFormat-entryAddr; /* * Fill in addresses of argument block and stack * (They happen to be t