1、操作系统课程设计75303 课程设计说明书学 院: 计算机科学与工程学院 专 业: 计算机科学与技术 姓 名: 天驹 学 号: 0900310327 指导教师: 黄廷辉 2012年 3 月 5 日操作系统课程设计报告GeekOS操作系统的研究与实现(项目0-项目2)一、 实验目的:熟悉GeekOS项目编译运行环境、核态进程的实现、用户态进程的实现、进程调度策略和算法实现、分页存储管理的实现和文件系统的实现等。二、 项目设计要求:GeekOS设计项目0:1. 搭建GeekOS的编译和调试平台,掌握GeekOS的核进程工作原理。2. 熟悉键盘操作函数,编程实现一个核进程。该进程的功能是:接受键盘输
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/u
3、sre.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结构。
4、 (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()函数的功能是为进程初始化核堆栈,堆栈中是为进程首次进入用户态运行时设置处理器状态要使用
5、的数据。 (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文件中改写生成第
6、一个用户态进程的函数调用: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操作系统环境;(二
7、) 联网后通过系统里的更新管理器更新了系统,并安装了语言包和必要的驱动。(三) 在ubuntu软件中心下载安装了NASM汇编器、Bochs PC模拟器以及bochs-x插件(保证ubuntu10.10环境下的bochs正常运行)。四、 项目设计原理: Make工作原理:在默认的方式下,只要输入make命令就可以工作。具体的处理过程如下:(1)make会在当前目录下找文件名为“Makefile”或“makefile”的文件。 (2)如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,它会找到“edit”这个文件,并把这个文件作为最终的目标文件。 (3)如果edit文件不存在,
8、或是edit所依赖的后面的.o文件的修改时间要比edit这个文件新,那么,就会执行后面所定义的命令来生成edit这个文件。 (4)如果edit所依赖的.o文件也不存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件(这有点像一个堆栈的过程)。 (5)如果指定的C文件和H文件是存在的, make会生成.o文件,然后再用.o文件生成make的最终任务,也就是生成执行文件edit。 GeekOS的makefile文件功能:(1) 指定GeekOS如何被编译,哪些源文件被编译,哪些用户程序被编译等等。通常不同项目的编译仅仅需要修改这一部分。(2) 定义了编译
9、GeekOS要用到的工具程序。(3) 指定规则:描述系统如何编译源程序。(4) 指定系统编译生成的指定文件 。GeekOS项目的开发流程:1. 开始一个GeekOS项目,第一步是添加相应的代码 。2. 在Linux下利用make命令编译系统,生成系统镜像文件。1 $ cd /project0/build2 $ make depend3 $ make 3. 编写每个项目相应的Bochs的配置文件。4. 运行Bochs模拟器,执行GeekOS核。1 $ cd /bochs 2 $ bochs 3 运行后,屏幕上会有一些提示。运行GeekOS选择Begin simulation,如果GeekOS 编
10、译成功,并且bochs的配置也没问题,将会看到一个模拟VGA的文本窗口,Geekos就能运行程序输出相应信息 (每个环境具体运行的命令格式会有一些不同)核线程的建立流程: 用户态进程创建流程 五、 项目设计的具体实现(程序代码):GeekOS设计项目0:Main.c文件:#include #include #include #include #include #include #include #include #include #include #include /* * Kernel C code entry point. * Initializes kernel subsystems,
11、mounts filesystems, * and spawns init process. */void Main(struct Boot_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(Welcom
12、e to GeekOS!n); Set_Current_Attr(ATTRIB(BLACK, GRAY); void EchoCount() Keycode keycode; int count; 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+d Is Entered! Pr
13、ogram Ended!); Exit(1); else if ( !(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(The counnts is %d
14、 ,count); Print(n); count=0; struct Kernel_Thread *kerThd;kerThd = Start_Kernel_Thread(&EchoCount, 0 , PRIORITY_NORMAL, false); /* Now this thread is done. */ Exit(0);GeekOS设计项目1:Elf.c文件:#include #include #include #include /* for debug Print() statements */#include #include #include #include /* * Fr
15、om the data of an ELF executable, determine how its segments * need to be loaded into memory. * param exeFileData buffer containing the executable file * param exeFileLength length of the executable file in bytes * param exeFormat structure describing the executables segments * and entry address; to
16、 be filled in * return 0 if successful, phoff); KASSERT(exeFileData!=NULL); KASSERT(exeFileLengthhead-ehsize+head-phentsize*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;
17、 exeFormat-segmentListi.lengthInFile=proHeader-fileSize; exeFormat-segmentListi.startAddress=proHeader-vaddr; exeFormat-segmentListi.sizeInMemory=proHeader-memSize; exeFormat-segmentListi.protFlags=proHeader-flags; proHeader+;return 0;GeekOS设计项目2:Src/GeekOS/user.c文件中的函数Spawn():int Spawn(const char *
18、program, const char *command, struct Kernel_Thread *pThread) /* * Hints: * - Call Read_Fully() to load the entire executable 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 sho
19、uld be loaded * - Call Load_User_Program() to create a User_Context with the loaded * program * - Call Start_User_Thread() with 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;
20、 ulong_t exeFileLength; struct User_Context *userContext = 0; struct Kernel_Thread *process = 0; struct Exe_Format exeFormat; /* * Load the executable file data, parse ELF headers, * and load code and data segments into user memory. */ if (rc = Read_Fully(program, (void*) &exeFileData, &exeFileLengt
21、h) != 0 | (rc = Parse_ELF_Executable(exeFileData, exeFileLength, &exeFormat) != 0 | (rc = Load_User_Program(exeFileData, exeFileLength, &exeFormat, command, &userContext) != 0) goto fail; /* * User program has been loaded, so we can free the * executable file data now. */ Free(exeFileData); exeFileD
22、ata = 0; /* Start the process! */ process = Start_User_Thread(userContext, false); if (process != 0) KASSERT(process-refCount = 2); /* Return Kernel_Thread pointer */ *pThread = process; else rc = ENOMEM; return rc;fail: if (exeFileData != 0) Free(exeFileData); if (userContext != 0) Destroy_User_Con
23、text(userContext); return rc;Src/GeekOS/user.c文件中的函数Switch_To_User_Context():void Switch_To_User_Context(struct Kernel_Thread* kthread, struct Interrupt_State* state) /* * Hint: Before executing in user mode, you will need to call * the Set_Kernel_Stack_Pointer() and Switch_To_Address_Space() * func
24、tions. */ static struct User_Context* s_currentUserContext; /* last user context used */ struct User_Context* userContext = kthread-userContext; /* * FIXME: could avoid resetting ss0/esp0 if not returning * to user space. */ KASSERT(!Interrupts_Enabled(); if (userContext = 0) /* Kernel mode thread:
25、no need to switch address space. */ return; /* Switch only if the user context is indeed different */ if (userContext != s_currentUserContext) ulong_t esp0; /* Switch to address space of user context */ Switch_To_Address_Space(userContext); /* * By definition, when returning to user mode there is no
26、 * context remaining on the kernel stack. */ esp0 = (ulong_t) kthread-stackPage) + PAGE_SIZE; /* Change to the kernel stack of the new process. */ Set_Kernel_Stack_Pointer(esp0); /* New user context is active */ s_currentUserContext = userContext; src/GeekOS/elf.c文件中的函数Parse_ELF_Executable():int Par
27、se_ELF_Executable(char *exeFileData, ulong_t exeFileLength, struct Exe_Format *exeFormat) elfHeader *hdr; programHeader *phdr; int i; hdr = (elfHeader *) exeFileData; /* * FIXME: when checking offsets, we really ought to be * checking overflow cases. Need to use functions from * range.h (which needs
28、 to be implemented, too) */ if (exeFileLength phnum EXE_MAX_SEGMENTS) if (elfDebug) Print(Too many segments (%d) in ELF executablen, hdr-phnum); return ENOEXEC; if (exeFileLength phoff + (hdr-phnum * sizeof(programHeader) if (elfDebug) Print(Not enough room for program headern); return ENOEXEC; exeFormat-numSegments = hdr-phnum; exeFormat-entryAdd
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1