1、关于Geekos操作系统论文项目03 编号:操作系统课程设计说明书题目:GeekOS操作系统的研究与实现 学院:计算机科学与工程学院专业:计算机科学与技术学生姓名:学号:指导教师:黄廷辉 2013年3月27日1 Geekos系统概述操作系统是管理系统软、硬件资源,控制程序运行,改善人机界面,提供各种服务,合理组织计算机工作流程和为用户有效使用计算机提供良好运行环境的系统软件,它为用户使用计算机提供一个灵活、安全、可靠的工作环境,也是其他应用软件赖以存在的基础。操作系统是计算机系统的重要组成部分,操作系统课程是计算机教育的必修课程,作为计算机专业的核心课程,不但高校计算机相关专业的学生必须学习操
2、作系统,从事计算机行业的从业人员也需深入了解它。GeekOS是一个基于X86架构的PC机上运行的微操作系统内核,由美国马理兰大学的教师开发,是一能够个用C语言开发的操作系统,GeekOS主要用于操作系统课程设计,目的是使学生能够实际动手参与到一个操作系统的开发工作中。出于教学目的,这个系统内核设计简单,却又兼备实用性,它可以运行在真正的X86PC硬件平台。作为一个课程设计平台,Geekos由一个基本的操作系统内核作为基础,提供了操作系统与硬件之间的所有必备接口,实现了系统引导、实模式到保护模式的转换、中断调用及异常处理、基于段式的内存管理、FIFO进程调度算法以及内核进程、基本输入输出(键盘作
3、为输入设备,显示器作为输出设备),以及一个用于存放用户程序的只读文件系统PFAT。2 建立开发环境2.1 在VirtualBox上安装Linux操作系统Geekos系统开发调试环境有多种选择:在Windows下使用Cygwin模拟Linux的开发环境;在PC机直接安装Linux进行开发调试或者在虚拟机上安装Linux进行开发调试。在这次操作系统课设中,我使用的是在虚拟机上安装Linux进行开发调试。步骤是:(1) 运行Oracle VM VirtualBox,选择“新建”,在新建虚拟电脑界面,将名称填为fedora,类型选择Linux,版本选择Fedora。(2) 点击下一步,默认内存大小为7
4、68MB。(3) 点击下一步,选择使用已有的虚拟硬盘文件,在文件夹中寻找fedora.vdi,并点击创建。2.2 安装编译软件编译Geekos需要用到很多工具,其中最主要的是gcc,nasm,bochs,make工具。因为这次课程设计使用了老师给的fedora虚拟机,虚拟机上已经全部配置好所有的工具,所以就不用额外安装这些编译工具,但是在这里还是简要地介绍一下编译软件的建立情况:(1) GCC在系统安装时就会自动安装,在这里就不详细介绍了。(2) nasm的安装:在终端输入sudo apt-get install nasm。(3) bochs的安装:在终端输入sudo apt-get inst
5、all bochs,在输入sudo apt-get install bochs-x。(4) make的安装:在终端输入sudo apt-get install make。3 项目03.1 实验目的熟悉GeekOS的项目编译、调试和运行环境,掌握GeekOS运行工作过程。3.2 项目设计要求(1) 搭建Geekos的编译和调试平台,掌握Geekos的内核进程工作原理。(2) 熟悉键盘操作函数,编程实现一个内核进程。该进程的功能是:接收键盘输入的字符并显示到屏幕上,当输入Ctrl+D时,结束进程的运行。(3) 编写一个C语言函数,函数功能是:接收键盘输入的按键,并将键值在显示器显示出来,当输入ct
6、rl+d就退出;(4) 在Main函数体内调用Start_Kernel_Thread函数,将步骤1编写的函数地址传递给参数startFunc,利用Setup_Kernel_Thread函数建立一个待运行的线程。(5) 在Linux环境下编译系统得到GeekOS镜像文件。(6) 编写一个相应的bochs配置文件。(7) 在bochs中运行GeekOS系统显示结果。3.3 项目设计原理Geekos的键盘处理函数是定义在keyboard.h与keyboard.c两个文件中。在keyboard.c中定义了F1F12,shift,alt等功能键常量,还定义了一个用于存放键盘扫描码的缓冲区。键盘处理初始化
7、是在main函数中调用Init_Keyboard函数进行的,Init_Keyboard主要功能是设置初始状态下存放键盘扫描码的缓冲区,并为键盘中断设置处理函数。从Init_Keyboard函数代码可以看到,任何的按键操作都会引发键盘中断处理。键盘中断处理过程是:首先从相应I/O端口读取键盘扫描码,根据是否按下shift键,分别在键值表中寻找扫描码对应的按键值,经过处理后将键值放入键盘缓冲区,最后通知系统重新调度进程。进程若需要获得键盘输入,只需要调用函数Wait_For_Key即可,进程调用该函数后,会阻塞进入按键操作的等待队列,直到按键操作结束,进程才被唤醒。3.4 项目设计的具体实现(1)
8、 在geekos-0.3.0srcproject0srcgeekosmain.c中添加一个函数。函数的功能是:接收键盘输入的按键,并将键值显示到显示器的函数,当输入Ctrl+D就退出。具体函数代码如下:void project0()Print(To Exit hit Ctrl+d.n );Keycode keycode;while(1)if(Read_Key(&keycode)if(!(keycode & KEY_SPECIAL_FLAG)|(keycode & KEY_RELEASE_FLAG)int asciiCode=keycode & 0xff;if(keycode & KEY_CTR
9、L_FLAG)=KEY_CTRL_FLAG & asciiCode=d)Print(n-goodbye!-n);Exit(1);elsePrint(%c,(asciiCode=r)?n:asciiCode);(2) 注释Main()函数中TODO,调用Start_Kernel_Thread函数,将步骤1编写的函数地址传递给参数startFunc,建立一个内核级进程。struct Kernel_Thread *thread;thread = Start_Kernel_Thread(&project0,0,PRIORITY_NORMAL,false);(3) 运行终端,在geekos-0.3.0s
10、rcproject0build,执行make depend及make命令,此时会在build目录下生成fd.img。(4) 在geekos-0.3.0srcproject0build目录下,在终端输入ls -a会看到.bochsrc文件,编写.bochsrc配置文件vgaromimage: /usr/share/bochs/VGABIOS-lgpl-latestromimage:file=$BXSHARE/BIOS-bochs-latestmegs: 8boot: afloppya: 1_44=fd.img, status=insertedlog: ./bochs.outkeyboard_se
11、rial_delay: 200vga_update_interval: 300000mouse: enabled=0private_colormap: enabled=0i440fxsupport: enabled=03.5 系统编译运行的原理及结果在geekos-0.3.0srcproject0build目录下,在终端输入bochs,即可看见以下界面。在以上界面下选择6,则会出现Geekos界面,输入ctrl+D,则会出现以下界面。4 项目14.1 实验目的熟悉ELF文件格式,了解GeekOS系统如何将ELF格式的可执行程序装入到内存,建立内核进程并运行的实现技术。4.2 项目设计要求(1)
12、 修改/geekos/elf.c文件:在函数Parse_ELF_Executable( )中添加代码,分析ELF格式的可执行文件(包括分析得出ELF文件头、程序头,获取可执行文件长度,代码段、数据段等信息),并填充Exe_Format数据结构中的域值。(2) 在Linux环境下编译系统得到GeekOS镜像文件。(3) 编写一个相应的bochs配置文件。(4) 在bochs中运行GeekOS系统显示结果。4.3 项目设计原理可执行文件有三个重要概念:编译、连接、加载。源程序文件首先被编译成目标文件,多个文件被连接成一个可执行文件,最后可执行文件被加载到内存运行。ELF(Executable an
13、d linking format)文件是UNIX系统实验室作为应用程序二进制接口而开发的可执行文件,是x86 Linux系统下的一种常用目标文件(object file)格式。ELF文件内容有两个平行视图,一个是从装入运行角度,另一个是从连接角度,格式如下表1。表1 ELF目标文件格式连接程序视图执行程序视图ELF 头部ELF 头部程序头部表(可选)程序头部表节区1 段1 . 节区n 段2 . . . 节区头部表节区头部表(可选)ELF文件在磁盘中的映象和在内存中的执行程序镜像的对应关系如下图:图1 ELF文件和内存中的可执行文件镜像内核进程的创建流程如下图2:图2 内核进程流程图ELF文件头
14、的结构定义在头文件elf.h中,用于描述整个文件的总体结构。程序头部对可执行文件和共享目标文件有意义,可执行文件或者共享目标文件的程序头部是一个结构数组,每个结构描述了一个段或者系统准备程序执行所必需的其他信息。在ELF头部的e_phentsize和e_phnum成员中给出了程序头部的大小和数量。Geekos用户程序是用户态进程的原型,由外壳程序与主程序组成,外壳程序在Entry.c中定义。4.4 项目设计的具体实现修改project/project1/src/geekos/elf.c文件:在函数Parse_ELF_Executable( )中添加代码,分析ELF格式的可执行文件(包括分析得出
15、ELF文件头、程序头,获取可执行文件长度,代码段、数据段等信息),并填充Exe_Format数据结构中的域值。1、 elf.c:将ELF格式的可执行程序装入到内存,建立内核进程并运行。int Parse_ELF_Executable(char *exeFileData, ulong_t exeFileLength, struct Exe_Format *exeFormat) int i; elfHeader *head=(elfHeader*)exeFileData; programHeader *proHeader=(programHeader *)(exeFileData+head-pho
16、ff); 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; exeFormat-segmentListi.lengthInFile=proHea
17、der-fileSize; exeFormat-segmentListi.startAddress=proHeader-vaddr; exeFormat-segmentListi.sizeInMemory=proHeader-memSize; exeFormat-segmentListi.protFlags=proHeader-flags; proHeader+;return 0;(5) 运行终端,在geekos-0.3.0srcproject1build,执行make depend及make命令,此时会在build目录下生成fd.img,diskc.img。(6) 在geekos-0.3.0
18、srcproject1build目录下,在终端输入ls -a会看到.bochsrc文件,编写.bochsrc配置文件romimage: file=$BXSHARE/BIOS-bochs-latestmegs: 8boot: afloppya: 1_44=fd.img, status=inserted#ata0:enable=1,ioaddr1=0x1f0,ioaddr2=0x3f0,irq=14ata0-master:type=disk,path=diskc.img,mode=flat,cylinders=0log: ./bochs.out4.5 系统编译运行的原理及结果在geekos-0.3
19、.0srcproject1build目录下,在终端输入bochs,即可看见以下界面。5 项目25.1 实验目的扩充GeekOS操作系统内核,使得系统能够支持用户级进程的动态创建和执行。5.2 项目设计要求(1) “src/GeekOS/user.c”文件中的函数Spawn(),其功能是生成一个新的用户级进程;(2) “src/GeekOS/user.c”文件中的函数Switch_To_User_Context(),调度程序在执行一个新的进程前调用该函数以切换用户地址空间;(3) “src/GeekOS/elf.c”文件中的函数Parse_ELF_Executable()。该函数的实现要求和项目
20、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()函数
21、的功能是通过将进程的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()
22、函数、Sys_PrintString()函数、Sys_GetKey()、Sys_SetAttr()、Sys_GetCursor()、Sys_PutCursor()、Sys_Spawn()函数、Sys_Wait()函数和Sys_GetPID( )函数。(7) 在main.c文件中改写生成第一个用户态进程的函数调用:Spawn_Init_Process(void) 。5.3 项目设计原理Geekos进程状态及转化:图3 GeekOS进程状态转换GeekOS系统最早创建的内核进程有Idle、Reaper和Main三个进程,它们由Init_Scheduler函数创建:最先初始化一个核态进程mainTh
23、read,并将该进程作为当前运行进程,函数最后还调用Start_Kernel_Thread 函数创建了两个系统进程Idle和Reaper。 所以,Idle、Reaper和Main三个进程是系统中最早存在的进程。在GeekOS中为了区分用户态进程和内核进程,在Kernel_Thread结构体中设置了一个字段 userContext,指向用户态进程上下文。对于内核进程来说,这个指针为空,而用户态进程都拥有自己的用户上下文(User_Context)。因此,在GeekOS中要判断一个进程是内核进程还是用户态进程,只要通过userContext字段是否为空来判断就可以了。每个用户态进程都拥有属于自己的
24、内存段空间,如:代码段、数据段、堆栈段等,每个段有一个段描述符(segment descriptor),并且每个进程有一个段描述符表(Local Descriptor Table),用于保存该进程的所有段描述符。操作系统中还设置一个全局描述符表(GDT,Global Descriptor Table),用于记录了系统中所有进程的ldt描述符。图10.2 GDT、LDT和User_Context的关系用户态进程创建LDT的步骤:(1)调用函数Allocate_Segment_Descriptor()新建一个LDT描述符;(2)调用函数Selector()新建一个LDT选择子;(3)调用函数Ini
25、t_Code_Segment_Descriptor()新建一个文本段描述符;(4)调用函数Init_Data_Segment_Descriptor()新建一个数据段;(5)调用函数Selector()新建一个数据段选择子;(6)调用函数Selector()新建一个文本(可执行代码)段选择子。用户态进程创建流程:5.4 项目设计的具体实现(1) “src/GeekOS/user.c”文件中的函数Spawn(),其功能是生成一个新的用户级进程;int Spawn(const char *program, const char *command, struct Kernel_Thread *pThr
26、ead) /TODO(Spawn a process by reading an executable from a filesystem); int rc; char *exeFileData=0; ulong_t exeFileLength; struct User_Context *userContext=0; struct Kenrnel_Thread *process=0; struct Exe_Format exeFormat; if(rc=Read_Fully(program,(void*)&exeFileData, &exeFileLength)!=0) Print(Faile
27、d to Read File %s!n,program); goto fail; if(rc=Parse_ELF_Executable(exeFileData,exeFileLength,&exeFormat)!=0) Print(Failed to Parse ELF File!n); goto fail; if(rc=Load_User_Program(exeFileData,exeFileLength,&exeFormat,command,&userContext)!=0) Print(Failed to Load User Program!n); goto fail; Free(exe
28、FileData); exeFileData=0; process=Start_User_Thread(userContext,false); if(process=0) rc=ENOMEM; Print(Start_User_Thread errorn); else / KASSERT(process-refCount=2); *pThread=process; /rc=process-pid; return rc;fail: if(exeFileData!=0) Free(exeFileData); if(userContext!=0) Destroy_User_Context(userC
29、ontext); return rc;(2) “src/GeekOS/user.c”文件中的函数Switch_To_User_Context(),调度程序在执行一个新的进程前调用该函数以切换用户地址空间;void Switch_To_User_Context(struct Kernel_Thread* kthread, struct Interrupt_State* state)/ TODO(Switch to a new user address space, if necessary); static struct User_Context*s_currentUserContext; st
30、ruct User_Context* userContext=kthread-userContext; KASSERT(!Interrupts_Enabled(); if(userContext=0) return; if(userContext!=s_currentUserContext) ulong_t esp0; Switch_To_Address_Space(userContext); esp0=(ulong_t)kthread-stackPage)+PAGE_SIZE; Set_Kernel_Stack_Pointer(esp0); s_currentUserContext=userContext; (3) “src/GeekOS/elf.c”文件中的函数Parse_ELF_Executable()。该函数的实现要求和项目1相同。int Parse_ELF_Executable(char *exeFileData, ulong_t exeFileLength, struct Exe_Format *exeFormat) int i; elfHeader *hd
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1