geekos计算机系统软件实验报告

更新时间:2024-05-20 14:42:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

计算机系统软件实验报告

GeekOS操作系统的研究与实现

题 目:GeekOS操作系统的研究与实现 学 院: 计算机科学与工程学院 专 业: 计算机技术 学生姓名:

学 号:

指导教师: 黄 庭 辉

题目类型:?理论研究 ?实验研究 ?工程设计 ?工程技术研究 ?软件开发

2011年 6 月 28 日

计算机系统软件实验报告

——GeekOS操作系统的研究与实现

一.实验目的

通过对一个教学操作系统GEEKOS的编译过程,形成磁盘映射文件,并在Bochs模拟器下模拟启动,来了解操作系统的基本原理和系统的启动过程。同时扩充GeekOS操作系统内核,使得系统能够支持用户级进程的动态创建和执行。

二.实验要求

GEEKOS提供了7个project进行试验,本人选做了project0~project2。 (1)搭建GeekOS的编译和调试平台,掌握GeekOS内核进程工作原理。

(2)了解GEEKOS的使用方法,完成其键盘响应函数,使其对键盘的中断作出响应,并显示

出按下的键值。

(3)完成对可执行文件(ELF)的格式分析,并将分析结果传递到GEEKOS提供的程序装载

器。

(4)在用户模式下建立进程和使用系统调用。 (5)实现进程的调度和同步。

三.实验及其配置

本实验所需资源:VMware ,Red Hat Enterprise Linux 4、Geekos-0.3.0、Bochs 2.2.6、Gedit、Nasm汇编语言编译器等。本实验采用直接在虚拟机上安装Linux进行开发调试

1 GeekOS简介

操作系统是管理系统软,硬件资源,控制程序运行,改善人机界面,提供各种服务,合理组

织计算机工作流程和为用户有效使用计算机提供良好运行环境的系统软件,它为用户使用计算机提供一个方便,灵活,安全,可靠的工作环境,也是其他应用软件赖以存在的基础.操作系统是计算机系统的重要组成部分,操作系统课程是计算机教育的必修课程,作为计算机专业的核心课程,不但高校计算机相关专业的学生必须学习操作系统,从事计算机行业的从业人员也需要深入了解它.

本文介绍GreekOS操作系统是一款在实际硬件上(x86-based PCs)运行的微操作系统内核。GreekOS是一款适合于教学的免费软件,其主要特点是:简单、实用、易懂,便于学生们理解操作系统的设计思想和实现过程。

因为它的使用者主要是教学者和学生,GreekOS是免费软件,并且有许多GeekOS下载站点。

GEEKOS仅仅包含了操作系统最基本的功能,GreekOS的特点如下: 1、中断处理; 2、堆存储分配;

3、静态优选权时序安排的时间片内核线程; 4、关于内核线程同步的互斥和条件变量;

5、基于分割保护的用户模式和一个简单的系统调用接口; 6、对键盘和VGA文本模式显示的设备启动;

目前,除上述所列的之外,还缺少虚拟内存、存储设备驱动和文件系统。在GeekOS中,使用分段机制实现了用户模式任务的内存保护。为了克服在存储设备和文件系统方面的欠缺,GeekOS提供了一个种机制以实现将用户程序编译成直接链接内核的数据对象。这种技术也可以用来实现基于RAM的文件系统。

2 VMware虚拟机 ,Bochs仿真器介绍

VMWare (Virtual Machine ware)是一个“虚拟PC”软件公司.它的产品可以使你在一台机器上同时运行二个或更多Windows、DOS、LINUX系统。与“多启动”系统相比,VMWare采用了完全不同的概念。多启动系统在一个时刻只能运行一个系统,在系统切换时需要重新启动机器。VMWare是真正“同时”运行,多个操作系统在主系统的平台上,就象标准Windows应用程序那样切换。而且每个操作系统你都可以进行虚拟的分区、配置而不影响真实硬盘的数据,你甚至可以通过网卡将几台虚拟机用网卡连接为一个局域网,极其方便。安装在VMware操作系统性能上比直接安装在硬盘上的系统低不少,因此,比较适合学习和测试。

Bochs仿真器其实质就是一台虚拟PC机,也就是所谓的“机中机”“Bochs 。for windows”窗口即Bochs仿真器的屏幕,上面有软盘、CDROM、Reset按钮、电源图标等。Bochs 是用 C++ 开发的可移植的 IA-32 (x86) PC 模拟器,几乎可以运行在所有流行的平台上。它包括对 Intel x86 CPU 、通用 I/O 设备和可定制的 BIOS 的模拟。目前, Bochs 可以模拟 386, 486, Pentium Pro 或者 AMD64 CPU ,包括可选的 MMX, SSE, SSE2 和 3DNow 指令。Bochs 的模拟环境中可以运行大部分的操作系统,包括 Linux, Windows 95, DOS, Windows NT 4, FreeBSD, MINIX 等。

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 开发环境搭建和Bochs的配置

试验可以分别在Windows环境和Linux环境下交替完成,下面介绍具体的实现方法。

1) 下载并安装VMware虚拟机;

2)在VMware虚拟机上安装linux,操作系统开发工具包里,有nasm软件。选中安装即可。如果安装系统没有安装。可以在以后的添加删除程序里安装,不过要安装盘。和windows安装其他软件类似。安装即可。

3)在VMware虚拟机上的linux环境下对GeekOS源文件进行编译和链接,编译成功后生成fd.img软盘映射文件和hd.img硬盘映射文件; 4) 下载并安装系统仿真工具Bochs;

5)配置Bochs环境配置文件Bochsrc.txt,

6)启动Boch环境,在提示的出现GeekOS的欢迎界面。 7)GeekOS系统编译和仿真成功。

8)开始安装好bochs之后,对bochsrc.txt进行配置,考虑到bochs自带

bochsrc-sample.txt的比较复杂,因此对其进行改写,方便日后阅读。且将该配置文件放在各个项目之下,方便进行修改。以project0为例,该项目的配置文件如下:

vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latest

romimage: file=$BXSHARE/BIOS-bochs-latest, address=0xf0000 megs: 8 boot: a

floppya: 1_44=fd.img, status=inserted log: ./bochs.out

keyboard_serial_delay: 200 floppy_command_delay: 500 vga_update_interval: 300000 ips: 1000000 mouse: enabled=0

private_colormap: enabled=0 i440fxsupport: enabled=0

ata0:enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14

ata0-master:type=disk,mode=flat,path=diskc.img,cylinders=40,heads=8,spt=64

四 项目设计原理

(一) project0的设计

1.设计原理

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

2.运行原理

(1) 首先定义GeekOS的键盘处理函数在keyboard.h与keyboard.c两个文件中。在

keyboard.c中定义了F1-F12、Shift、Alt等功能键常量,还定义了一个用于存放键盘扫描码的缓冲区。

(2) 然后进行键盘初始化处理(Init_Keyboard函数在Main中)。

(3) 键盘中断的处理过程是:首先从相应I/O端口读取键盘扫面码,在根据是否按下Shift键,分别在键值表中寻找扫描码对应的按键值,经过处理后将键值放入键盘缓冲区,最后系统进行重新调度进程。

3.项目设计要求

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

2、熟悉键盘操作函数,编程实现一个内核进程。该进程的功能是:接收键盘输入的字符并

显示到屏幕上,当输入ctrl+d时,结束进程的运行。

4.实现步骤

1.编写一个C语言函数,函数功能是:接收键盘输入的按键,并将键值在显示器显示出来,

当输入ctrl+d就退出;

2.在Main函数体内调用Start_Kernel_Thread函数,将步骤1编写的函数地址传递给参数 startFunc,利用Setup_Kernel_Thread函数建立一个待运行的线程。 3.在Linux环境下编译系统得到GeekOS镜像文件。 4.编写一个相应的bochs配置文件。

5.编译GeekOS

进入geekos-0.3.0/src/project0/build目录 执行 make depend 执行 make

成功之后在build 目录下生成fd.img文件。

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

启动bochs

在build目录中执行 bochs –f bochsrc

7.运行结果

(项目 0)

8.核心代码

static void Keyboard_Interrupt_Handler(struct Interrupt_State* state) {

uchar_t status, scanCode; unsigned flag = 0; bool release = false, shift; Keycode keycode; Begin_IRQ(state);

status = In_Byte(KB_CMD); IO_Delay();

if ((status & KB_OUTPUT_FULL) != 0) { /*

* Print(\

/* There is a byte available */ scanCode = In_Byte(KB_DATA); IO_Delay();

*/

if (scanCode & KB_KEY_RELEASE) { release = true;

scanCode &= ~(KB_KEY_RELEASE); }

if (scanCode >= SCAN_TABLE_SIZE) {

Print(\, scanCode); goto done; }

/* Process the key */

shift = ((s_shiftState & SHIFT_MASK) != 0);

keycode = shift ? s_scanTableWithShift[scanCode] : s_scanTableNoShift[scanCode]; /* Update shift, control and alt state */ switch (keycode) { case KEY_LSHIFT: flag = LEFT_SHIFT; break;

case KEY_RSHIFT: flag = RIGHT_SHIFT; break; case KEY_LCTRL: flag = LEFT_CTRL; break; case KEY_RCTRL: flag = RIGHT_CTRL; break; case KEY_LALT: flag = LEFT_ALT; break; case KEY_RALT: flag = RIGHT_ALT; break; default:

goto noflagchange; }

if (release)

s_shiftState &= ~(flag); else

s_shiftState |= flag; /*

* Shift, control and alt keys don't have to be * queued, flags will be set! */ goto done;

noflagchange:

/* Format the new keycode */ if (shift)

keycode |= KEY_SHIFT_FLAG; if ((s_shiftState & CTRL_MASK) != 0) keycode |= KEY_CTRL_FLAG; if ((s_shiftState & ALT_MASK) != 0) keycode |= KEY_ALT_FLAG; if (release)

keycode |= KEY_RELEASE_FLAG;

/* Put the keycode in the buffer */ Enqueue_Keycode(keycode); /* Wake up event consumers */ Wake_Up(&s_waitQueue); /*

* Pick a new thread upon return from interrupt * (hopefully the one waiting for the keyboard event) */

g_needReschedule = true;

} done:

End_IRQ(state); }

(二) project1的设计

1.设计原理

在函数Parse_ELF_Executable()中添加代码,分析ELF格式的可执行文件(包括分析得出ELF文件头、程序头,获取可执行文件长度,代码段、数据段等信息),并填充Exe_Format数据中的值域。熟悉ELF文件格式,了解GeekOS系统如何将ELF格式的可执行程序装入到内存,建立内核进程并运行的实现技术。

2.运行原理

(1) 从用户程序的入口_Entry进入,而不是Main进入(Main函数实际是开发这定义的

程序主函数的名称)。

(2) 在GeekOS启动后,从PFAT文件系统讲执行文件装入内存,建立进程并运行得到

相应的输出。

(3) 用户可以看到函数调用Spawn_Init_Process,该函数的功能是调用

Start_Kernel_Thread(),它以Spawner函数为进程体建立一个核心级进程,并使之准备就绪。

(4) 函数Spawner主要功能从PFAT文件系统读入一个用户执行程序,为之建立相应的

进程影像。然后调用Spawn Program函数建立相应进程。

3.项目设计要求

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

2、在Linux环境下编译系统得到GeekOS镜像文件。 3、编写一个相应的bochs配置文件。

4、在bochs中运行GeekOS系统显示结果。

在此项目中,GeekOS需要从磁盘加载一个可执行文件到内存中,并且在内核线程中执行此程序。我们需要做的就是完成/geekos/elf.c中Parse_ELF_Executable函数,把EXE文件的内容填充到指定的Exe_format格式的区域。

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

int Parse_ELF_Executable(char *exeFileData, ulong_t exeFileLength, struct Exe_Format *exeFormat)

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

4.运行结果

(项目1)

5.核心代码

static int Spawn_Program(char *exeFileData, struct Exe_Format *exeFormat) {

struct Segment_Descriptor* desc;

unsigned long virtSize;

unsigned short codeSelector, dataSelector; int i;

ulong_t maxva = 0;

/* Find maximum virtual address */

for (i = 0; i < exeFormat->numSegments; ++i) {

struct Exe_Segment *segment = &exeFormat->segmentList[i]; ulong_t topva = segment->startAddress + segment->sizeInMemory; if (topva > maxva) maxva = topva; }

/* setup some memory space for the program */

virtSize = Round_Up_To_Page(maxva) + 4096; /* leave some slack for stack */ virtSpace = Malloc(virtSize);

memset((char *) virtSpace, '\\0', virtSize); /* Load segment data into memory */

for (i = 0; i < exeFormat->numSegments; ++i) {

struct Exe_Segment *segment = &exeFormat->segmentList[i]; memcpy(virtSpace + segment->startAddress,

exeFileData + segment->offsetInFile, segment->lengthInFile);

}

/* allocate and init descriptors and selectors for code and data */ // Kernel code segment.

desc = Allocate_Segment_Descriptor(); Init_Code_Segment_Descriptor(

desc,

(unsigned long)virtSpace, // base address (virtSize/PAGE_SIZE)+10, 0 );

// num pages

// privilege level (0 == kernel)

codeSelector = Selector( 0, true, Get_Descriptor_Index( desc ) ); // Kernel data segment.

desc = Allocate_Segment_Descriptor(); Init_Data_Segment_Descriptor(

desc,

(unsigned long)virtSpace, // base address (virtSize/PAGE_SIZE)+10, 0 );

// num pages

// privilege level (0 == kernel)

dataSelector = Selector( 0, true, Get_Descriptor_Index( desc ) ); Install_Interrupt_Handler( 0x90, &Printrap_Handler ); if (lprogdebug) {

Print(\); Print(\ = %x\\n\, (unsigned int) virtSpace); Print(\ = %x\\n\, (unsigned int) virtSize); Print(\, codeSelector); Print(\, dataSelector); Print(\); }

Trampoline(codeSelector, dataSelector, exeFormat->entryAddr); return 0; }

/* Spawner is a a thread that accomodates the program to be loaded & executed * it is started from main.c */

void Spawner( unsigned long arg ) {

const char *program = \; char *exeFileData = 0; ulong_t exeFileLength; struct Exe_Format exeFormat; /*

* Load the executable file data, parse ELF headers, * and load code and data segments into user memory. */

if (lprogdebug) {

Print(\, program); }

if (Read_Fully(program, (void**) &exeFileData, &exeFileLength) != 0) {

Print(\, program); goto fail; }

if (lprogdebug) {

Print(\); }

if (Parse_ELF_Executable(exeFileData, exeFileLength, &exeFormat) != 0) {

Print(\); goto fail; }

if (lprogdebug) {

Print(\);

}

if (Spawn_Program(exeFileData, &exeFormat) != 0) {

Print(\); goto fail; } /*

* User program has been loaded, so we can free the * executable file data now. */

Free(exeFileData); exeFileData = 0;

/* If we arrived here, everything was fine and the program exited */ Print(\); Print(\); Exit(0); fail:

/* We failed; release any allocated memory */ Disable_Interrupts(); Free(virtSpace); Enable_Interrupts(); }

(三) project2的设计

1.设计原理

Entry.c为用户程序外壳,用户程序的入口就在这里,并与用户程序一起编译。再根据传递的中断向量查找并调用相关的中断处理程序,并实现调度进程的选择。

2.运行原理

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

(2) 再用函数Switch_To_User_Context(),调度程序在执行一个新的进程前调用该函数以

切换用户的地址空间。

(3) 函数Parse_ELF_Executable()实现要求同项目1。

(4) Userseg.c文件是实现一些为实现对user.c中高层操作支持的函数。其中包括:释放

用户态进程占用的内存资源、通过加载可执行文件镜像创建新进程的User_Context结构、在用户地址空间和内核地址空间之间复制数据、通过将进程的LDT装入到LDT寄存器来激活用户的地址空间。

(5) 为进程初始化内核堆栈,其函数为Setup_User_Thread()。同时创建用户上下问对象,

其函数为Start_User_Thread()。

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

(7) 内核线程的建立流程:

Spawn_Init_Process() Start_Kernel_Thread() Spawner() Read_Fully() Parse_ELF_Executable() Spawn_Program()

(图 1)

3.运行结果

(项目 2)

4.核心代码

int Parse_ELF_Executable(char *exeFileData, ulong_t exeFileLength, struct Exe_Format *exeFormat) {

//By CQQ;2007-08-27---------------------------------------

int i;

elfheader=(elfHeader *)Malloc(sizeof(*elfheader)); { }

memcpy(elfheader,exeFileData,sizeof(*elfheader));

Print(\); return 1;

elfHeader *elfheader; if (elfheader==0)

exeFormat->numSegments=elfheader->phnum; //By CQQ;ELF文件的段的个数;

exeFormat->entryAddr=elfheader->entry; //BY CQQ;代码入//Print(\for (i=0;inumSegments ;i++ ) {

programHeader *phearder; if (phearder==0) { }

Print(\); return 1;

口地址;

phearder=(programHeader *)Malloc(sizeof(*phearder));

memcpy(phearder,exeFileData+sizeof(*elfheader)+i*sizeof(*phearder),sizeof(*phearder)); struct Exe_Segment *segment = &exeFormat->segmentList[i];

segment->offsetInFile=phearder->offset; //BY CQQ;段的偏移量;

segment->lengthInFile=phearder->fileSize; //BY CQQ;段在文件中所占的大小; }

int Spawn(const char *program, const char *command, struct Kernel_Thread **pThread) { /* * Hints:

* - Call Read_Fully() to load the entire executable into a memory buffer

} return 0;

segment->startAddress=phearder->vaddr; //BY CQQ;段在内存中的地址, segment->sizeInMemory=phearder->memSize; //BY CQQ;段在内存中的大小; segment->protFlags=phearder->flags; //BY CQQ;段的相关标志;

虚拟地址;

* - 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() 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. */

//By CQQ;---------------------------------------------------------------------- int rc=0;

char *exeFileData = 0; ulong_t exeFileLength; struct Exe_Format exeFormat;

struct User_Context *pUserContext=0;

if (Read_Fully(program, (void**) &exeFileData, &exeFileLength) != 0) struct Kernel_Thread *uThread=0; {

Print(\, program); goto fail; }

if (Parse_ELF_Executable(exeFileData, exeFileLength, &exeFormat) != 0) {

Print(\); goto fail; }

if (Load_User_Program(exeFileData, exeFileLength, &exeFormat, command, { }

Free(exeFileData);

uThread=Start_User_Thread(pUserContext, false); if (uThread==0) { }

KASSERT(uThread->refCount == 2); /* Return Kernel_Thread pointer */

rc = ENOMEM;

Print(\); //goto fail;

Print(\);

&pUserContext)!= 0)

goto fail;

exeFileData = 0;

*pThread=uThread; return rc; fail:

if (exeFileData != 0) {

Free(exeFileData); } { } return rc; } /*

* If the given thread has a User_Context, * switch to its memory space. *

* Params:

* kthread - the thread that is about to execute

* state - saved processor registers describing the state when * the thread was interrupted */

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() * functions. */

//By CQQ;-------------------------------------------------------------------------- static struct User_Context* mUserContext;

struct User_Context* userContext = kthread->userContext;

if (userContext == 0)

Set_Kernel_Stack_Pointer(((ulong_t) kthread->stackPage) + PAGE_SIZE);

if (userContext != mUserContext) {

return;

KASSERT(!Interrupts_Enabled());

Destroy_User_Context(pUserContext); exeFileData=0;

if (pUserContext != 0)

pUserContext=0;

Switch_To_Address_Space(userContext); //By CQQ;用户地址空间;

mUserContext = userContext; }

return; }

int Load_User_Program(char *exeFileData, ulong_t exeFileLength, struct Exe_Format *exeFormat, const char *command, struct User_Context **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 code entry point

* address, argument block address, and initial kernel stack pointer * address */

//struct Segment_Descriptor* desc; //unsigned long virtSize;

//unsigned long *argsSize=0; //unsigned *numArgs=0;

//unsigned short codeSelector, dataSelector; struct User_Context *mUserContext = 0; int i;

ulong_t maxva = 0;

unsigned numArgs; ulong_t virtSize; ulong_t *argsSize=0; //ulong_t argsAddr;

Set_Current_Attr(ATTRIB(BLACK, RED|BRIGHT)); Print(\); //Find maximum virtual address

//根据exeFormat确定分配给exe文件段多少内存空间;

Set_Current_Attr(ATTRIB(BLACK, GRAY));

for (i = 0; i < exeFormat->numSegments; ++i) {

struct Exe_Segment *segment = &exeFormat->segmentList[i]; ulong_t topva = segment->startAddress + segment->sizeInMemory; if (topva > maxva) maxva = topva; }

//分配内存空间(exe文件段空间+堆栈段空间+参数块大小); Get_Argument_Block_Size(command,&numArgs,argsSize);

//virtSize = Round_Up_To_Page(maxva) + DEFAULT_USER_STACK_SIZE ;

virtSize = Round_Up_To_Page(maxva) +

Round_Up_To_Page(DEFAULT_USER_STACK_SIZE+(*argsSize) ); /* leave some slack for stack */

virtSpace = Malloc(virtSize); if (virtSpace == 0) goto fail;

memset((char *) virtSpace, '\\0', virtSize);

//create the User_Context… if (mUserContext == 0) return -1;

mUserContext = Create_User_Context(sizeof(*mUserContext)); Format_Argument_Block( argsAddr, numArgs, (ulong_t) virtSpace+ virtSize-DEFAULT_USER_STACK_SIZE-(*argsSize) , command); /* Load segment data into memory */

for (i = 0; i < exeFormat->numSegments; ++i) {

struct Exe_Segment *segment = &exeFormat->segmentList[i]; memcpy(virtSpace + segment->startAddress,

exeFileData + segment->offsetInFile, segment->lengthInFile);

}

memcpy(virtSpace +virtSize-(*argsSize)-DEFAULT_USER_STACK_SIZE, argsAddr, (*argsSize));

//By CQQ;填充mUserContext内容;

//memset(mUserContext->memory, '\\0', virtSize); //mUserContext->size = virtSize; //desc = Allocate_Segment_Descriptor();

mUserContext->ldtDescriptor = Allocate_Segment_Descriptor(); //BY CQQ;初始化段描述;

Init_LDT_Descriptor(mUserContext->ldtDescriptor, mUserContext->ldt,

//BY CQQ;为mUserContext分配LDT段描述;

if (mUserContext->ldtDescriptor == 0) goto fail;

NUM_USER_LDT_ENTRIES);

mUserContext->ldtSelector = Selector(KERNEL_PRIVILEGE, true, Get_Descriptor_Index(mUserContext->ldtDescriptor));

Init_Null_Segment_Descriptor( &((mUserContext)->ldt[0]) ); //By CQQ;代码段;

Init_Code_Segment_Descriptor(

Init_Null_Segment_Descriptor( &((mUserContext)->ldt[1]) );

&mUserContext->ldt[0], (ulong_t) virtSpace, virtSize / PAGE_SIZE, USER_PRIVILEGE );

//By CQQ;数据段;

Init_Data_Segment_Descriptor( &mUserContext->ldt[1],

(ulong_t) virtSpace, virtSize / PAGE_SIZE, USER_PRIVILEGE );

mUserContext->csSelector = Selector(USER_PRIVILEGE, false, 0); mUserContext->dsSelector = Selector(USER_PRIVILEGE, false, 1);

mUserContext->memory=virtSpace; mUserContext->size=virtSize;

mUserContext->entryAddr=exeFormat->entryAddr; mUserContext->argBlockAddr=(ulong_t) argsAddr; mUserContext->stackPointerAddr=(unsigned

long)virtSpace+virtSize-DEFAULT_USER_STACK_SIZE; /* Nobody is using this user context yet */ mUserContext->refCount = 0;

*pUserContext = mUserContext; /*

//By CQQ;代码段;

desc = Allocate_Segment_Descriptor(); Init_Code_Segment_Descriptor(

desc,

(unsigned long)virtSpace, // base address (virtSize/PAGE_SIZE)+10, 0 );

// num pages

// privilege level (0 == kernel)

codeSelector = Selector( 0, true, Get_Descriptor_Index( desc ) );

//By CQQ;数据段;

desc = Allocate_Segment_Descriptor();

desc,

(unsigned long)virtSpace, // base address (virtSize/PAGE_SIZE)+10, 0 );

// num pages

// privilege level (0 == kernel)

Init_Data_Segment_Descriptor(

dataSelector = Selector( 0, true, Get_Descriptor_Index( desc ) ); */

/* Format argument block */

//Format_Argument_Block(mUserContext->memory + argsAddr, numArgs, argsAddr, command);

/* Fill in code entry point */

//mUserContext->entryAddr = exeFormat->entryAddr; /*

* Fill in addresses of argument block and stack * (They happen to be the same) */

//mUserContext->argBlockAddr = argsAddr; //mUserContext->stackPointerAddr = argsAddr;

//Print(\ return 0; fail:

if (mUserContext != 0) {

if (mUserContext->memory != 0)

{

Free(mUserContext->memory); mUserContext->memory=0; } /*

* Copy data from user memory into a kernel buffer. * Params:

* destInKernel - address of kernel buffer * srcInUser - address of user buffer * bufSize - number of bytes to copy *

* Returns:

* true if successful, false if user buffer is invalid (i.e., * doesn't correspond to memory the process has a right to * access) */

bool Copy_From_User(void* destInKernel, ulong_t srcInUser, ulong_t bufSize) { /* * Hints:

* - the User_Context of the current process can be found * from g_currentThread->userContext

* - the user address is an index relative to the chunk * of memory you allocated for it

* - make sure the user buffer lies entirely in memory belonging * to the process */

struct User_Context* userContext = g_currentThread->userContext; if (!Validate_User_Memory(userContext, srcInUser, bufSize)) return false; memcpy(destInKernel, (userContext->memory + srcInUser), bufSize);

}

Destroy_User_Context(mUserContext); mUserContext=0;

//Free(mUserContext); }

return -1;

return true;

TODO(\);

Validate_User_Memory(NULL,0,0); /* delete this; keeps gcc happy */ } /*

* Copy data from kernel memory into a user buffer. * Params:

* destInUser - address of user buffer * srcInKernel - address of kernel buffer * bufSize - number of bytes to copy *

* Returns:

* true if successful, false if user buffer is invalid (i.e., * doesn't correspond to memory the process has a right to * access) */

bool Copy_To_User(ulong_t destInUser, void* srcInKernel, ulong_t bufSize) { /*

* Hints: same as for Copy_From_User() */

struct User_Context* userContext = g_currentThread->userContext; memcpy((userContext->memory + destInUser), srcInKernel, bufSize); if (!Validate_User_Memory(userContext, destInUser, bufSize)) return false; return true;

TODO(\); } /*

* Switch to user address space belonging to given * User_Context object. * Params:

* userContext - the User_Context */

void Switch_To_Address_Space(struct User_Context *userContext) { /*

* Hint: you will need to use the lldt assembly language instruction * to load the process's LDT by specifying its LDT selector. */

__asm__ __volatile__ (

\:

: \ (userContext->ldtSelector)

);

本文来源:https://www.bwwdw.com/article/cjh7.html

Top