操作系统课设报告-桂林电子科技大学_第1页
操作系统课设报告-桂林电子科技大学_第2页
操作系统课设报告-桂林电子科技大学_第3页
操作系统课设报告-桂林电子科技大学_第4页
操作系统课设报告-桂林电子科技大学_第5页
已阅读5页,还剩21页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

《GeekOS操作系统》课程设计说明书题目:GeekOS操作系统的研究与实验学院:计算机科学与工程学院专业:信息平安姓名:学号:指导教师:2015年06月12日目录1GeekOS简介 11.1GeekOS系统源代码结构 12课程设计环境 23工程0的设计实现 33.1工程设计目的 33.2工程设计要求 33.3工程实现原理 33.4工程实现过程 33.5运行结果 54工程1的设计实现 64.1工程设计目的 64.2工程设计要求 64.3工程实现原理 64.4工程实现过程 84.5运行结果 95工程2的设计实现 105.1工程设计目的 105.2工程设计目的 105.3工程实现原理 115.4工程实现过程 135.5运行结果 236遇到问题及解决方法 237课程设计总结 241GeekOS简介GeekOS是一个基于X86架构的PC上运行的微操作系统内核,由美国马理兰大学的教师开发,主要用于操作系统课程设计,目的是使学生能够实际动手参与到一个操作系统的开发工作中。出于教学目的,这个系统内核设计简单,却又兼备实用性,它可以运行在真正的X86PC硬件平台。作为一个课程设计平台,GeekOS由一个根本的操作系统内核作为根底,提供了操作系统与硬件之间的所有必备接口,实现了系统引导,实模式到保护模式的转换,中断调用及异常处理,基于段式的内存管理,FIFO进程调度算法以及内核进程,根本的输入输出(键盘作为输入设备,显示器作为输出设备),以及一个用于存放用户程序的只读文件系统PFAT。学生可以在Linux或Unix环境下对其进行功能扩充,且其针对进程、文件系统、存储管理等操作系统核心内容分别设计了7个难度逐渐增加的工程供学生选择。1.1GeekOS系统源代码结构GeekOS操作系统源文件可以从下载。图1.1GeekOS系统主目录在doc目录里的文件hacking.pdf和index.htm是GeekOS系统的参考文档。Scripts目录下有startProject和removeEmptyConflicts两个脚本文件。GeekOS系统的源文件在src目录下,分为7个工程:Project0到Project7。在build文件夹中,包含系统编译后的可执行文件的文件、软盘镜像或是硬盘镜像、makefile工程管理文件。在include文件夹中有GeekOS和libc两个子目录,在GeekOS子目录中有kthread.h、keyboard.h等文件。图1.2工程文件结构图2课程设计环境1.虚拟机软件:VMwareWorkstation10.0。2.虚拟系统:linux系统CentOS6.0。3.NASM汇编器。4.GNUgcc编译器。5.GNUgdb调试器。6.SourseInsight:程序编辑器和代码浏览器。7.Bochs:GeekOS运行于Windows(或Linux)下的BochsPC模拟器,Bochs是用C++开发的可移植的IA-32(x86)PC模拟器,它包括对Intelx86CPU、通用I/O设备和可定制的BIOS的模拟,几乎可以运行在所有流行的平台上。在本次课设中使用的是bochs2.6。图2.1课设环境3工程0的设计实现3.1工程设计目的熟悉GeekOS的工程编译、调试和运行环境,掌握GeekOS运行工作过程。3.2工程设计要求1.搭建GeekOS的编译和调试平台,掌握GeekOS的内核进程工作原理。2.熟悉键盘操作函数,编程实现一个内核进程。该进程的功能是:接收键盘输入的字符并显示到屏幕上,当输入ctrl+d时,结束进程的运行。3.3工程实现原理工程0主要要求设计一个函数对键盘的中断进行响应。这主要通过使用GeekOS提供的键盘响应函数Wait_Kernel_Thread进行键盘中断的响应和返回键值。该函数首先检查键盘缓冲区是否有按键,如果有,就读取一个键码,如果此时键盘缓冲区没有键值,就将线程放入键盘事件等待队列。于是可分为两步完成:1.编写函数EchoCount,函输功能是:接受键盘输入的按键,并将键值显示在显示器,当输入Ctrl+D退出。2.在Main函数体内调用Start_Kernel_Thread函数,将编写的函数地址传递给startFunc,建立一个内核进程。3.4工程实现过程1.添加代码〔1〕在Main函数中编写一个函数,函数功能是:接收键盘输入的按键,并将键值显示到显示器的函数,当输入Ctrl+D就退出。voidproject0()

{Print("ToExithitCtrl+d.\n");

Keycodekeycode;

while(1)

{

if(Read_Key(&keycode))

{//读取键盘按键状态if(!((keycode&KEY_SPECIAL_FLAG)||(keycode&KEY_RELEASE_FLAG)))

{//只处理非特殊按键的按下事件

intasciiCode=keycode&0xff;//低8位为Ascii码

if((keycode&KEY_CTRL_FLAG)==KEY_CTRL_FLAG&&asciiCode=='d'){//按下Ctrl键Print("\nBYE!\n");

Exit(1);

}else{

Print("%c",(asciiCode=='\r')?'\n':keycode);

}

}

}

}

}//放在main函数之前

〔2〕

在Main函数体内调用Start_Kernel_Thread函数,将步骤1编写的函数地址传递给参数startFunc,建立一个内核级进程。

voidMain(structBoot_Info*bootInfo)

{

//TODO("Startakernelthreadtoechopressedkeysandprintcounts");

structKernel_Thread*thread;

thread=Start_Kernel_Thread(&project0,0,PRIORITY_NORMAL,false);

}2.编译GeekOS工程project0〔1〕shell>>#cd/…〔2〕shell>>#makedepend生成depend.mak文件。图3.1makedepend执行过程〔3〕shell>>#make成功之后在build目录下生成fd.img文件。图3.2make执行过程3.配置启动Bochs〔1〕创立bochs配置文件shell>>#geditbochsrc〔2〕在编辑器中输入一下配置内容gdbstub:enabled=1,port=1234,text_base=0,data_base=0,bss_base=0romimage:file=$BXSHARE/BIOS-bochs-latestmegs:8boot:afloppya:1_44=fd.img,status=insertedlog:./bochs.out〔3〕保存,直接退出gedit3.5运行结果〔1〕启动bochsshell>>#bochs-bochsrc〔2〕选择beginsimulation〔3〕结果:图3.3工程0运行结果4工程1的设计实现4.1工程设计目的熟悉ELF文件格式,了解GeekOS系统如何将ELF格式的可执行程序装入到内存,建立内核进程并运行的实现技术。4.2工程设计要求1.修改/geekos/elf.c文件:在函数Parse_ELF_Executable()中添加代码,分析ELF格式的可执行文件〔包括分析得出ELF文件头、程序头,获取可执行文件长度,代码段、数据段等信息〕,并填充Exe_Format数据结构中的域值。2.在Linux环境下编译系统得到GeekOS镜像文件。3.编写一个相应的bochs配置文件。4.在bochs中运行GeekOS系统显示结果。4.3工程实现原理1.ELF文件格式。连接程序视图执行程序视图ELF头部ELF头部程序头部表〔可选〕程序头部表节区1段1…节区n段2………节区头部表节区头部表〔可选〕表4.1ELF目标文件格式2.内存中的可执行文件镜像GeekOS中的用户进程全部在系统的编译阶段完成编译和链接,形成可执行文件,用户可执行文件保存在PFAT文件系统中。工程1要完成的事系统启动后,从PFAT文件系统将可执行文件装入内存,建立进程并运行得到相应的输出。如下列图:图4.1文件镜像3.内核线程的建立流程该过程主要由Spawner函数实现,其主要经过:调用Read_Fully函数将文件读入内存,后调用Parse_ELF_Executable函数分析ELF文件,最后调用Spawn_Program函数将可执行程序的代码段和数据段等装入内存,此后便可以开始运行一个内核级进程了。如下列图:图4.2建立流程4.4工程实现过程1.添加代码修改/geekos/elf.c文件。

在函数Parse_ELF_Executalbe()中添加代码,分析ELF格式的可执行文件〔包括分析得出ELF文件头、程序头,获取可执行文件长度,代码段、数据段等信息〕,并填充Exe_Format数据结构中的域值。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;i<head->phnum;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;

}2.编译GeekOS工程project0〔1〕执行makedependshell>>#makedepend生成depend.mak文件〔2〕执行makeshell>>#make成功之后再build目录下生成fd.img和disk.img文件。3.配置启动Bochs〔1〕创立bochs配置文件shell>>#geditbochsrc〔2〕在编辑器输入一下配置内容romimage:file=$BXSHARE/BIOS-bochs-latestmegs:8boot:afloppya:1_44=fd.img,status=insertedata0-master:type=disk,mode=flat,path="diskc.img",cylinders=0log:./bochs.out〔3〕保存,直接退出gedit4.5运行结果〔1〕启动bochsshell>>#bochs–fbochsrc 〔2〕选择beginsimulation 〔3〕结果:图4.3工程1运行结果5工程2的设计实现5.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〔〕。该函数的实现要求和工程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〔〕函数的功能是通过将进程的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〔〕函数、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工程实现原理1.GeekOS进程状态及转换图5.1GeekOS进程转换GeekOS系统最早创立的内核进程有Idle、Reaper和Main三个进程,它们由Init_Scheduler函数创立:最先初始化一个核态进程mainThread,并将该进程作为当前运行进程,函数最后还调用Start_Kernel_Thread函数创立了两个系统进程Idle和Reaper。所以,Idle、Reaper和Main三个进程是系统中最早存在的进程。2.GeekOS的用户态进程在GeekOS中为了区分用户态进程和内核进程,在Kernel_Thread结构体中设置了一个字段userContext,指向用户态进程上下文。对于内核进程来说,这个指针为空,而用户态进程都拥有自己的用户上下文〔User_Context〕。因此,在GeekOS中要判断一个进程是内核进程还是用户态进程,只要通过userContext字段是否为空来判断就可以了。图5.2用户进程3.用户进程空间每个用户态进程都拥有属于自己的内存段空间,如:代码段、数据段、堆栈段等,每个段有一个段描述符〔segmentdescriptor〕,并且每个进程有一个段描述符表〔LocalDescriptorTable〕,用于保存该进程的所有段描述符。操作系统中还设置一个全局描述符表〔GDT,GlobalDescriptorTable〕,用于记录了系统中所有进程的ldt描述符。图5.3用户进程空间4.用户态进程创立LDT的步骤(1)调用函数Allocate_Segment_Descriptor()新建一个LDT描述符;(2)调用函数Selector()新建一个LDT选择子;(3)调用函数Init_Code_Segment_Descriptor()新建一个文本段描述符;(4)调用函数Init_Data_Segment_Descriptor()新建一个数据段;(5)调用函数Selector〔〕新建一个数据段选择子;(6)调用函数Selector〔〕新建一个文本〔可执行代码〕段选择子。图5.4用户进程创立流程5.4工程实现过程1.添加代码〔1〕修改src/GeekOS/user.c文件中的函数Spawn(),其功能是生成一个用户级进程。〔2〕src/GeekOS/user.c文件中的函数Switch_To_User_Contex(),调度程序在执行一个新的进程前调用该函数以切换用户地址空间。〔3〕src/GeekOS/elf.c文件中的函数Prase_ELF_Executable()。该函数的实现要求和工程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〔〕函数的功能是通过将进程的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相关函数的修改。〔7〕src/GeekOS/syscall.c”文件中主要是实现用户程序要求内核进行效劳的一些系统调用函数定义。

要求用户实现的有Sys_Exit〔〕函数、Sys_PrintString〔〕函数、Sys_GetKey〔〕、Sys_SetAttr〔〕、Sys_GetCursor〔〕、Sys_PutCursor〔〕、Sys_Spawn〔〕函数、Sys_Wait〔〕函数和Sys_GetPID()函数。〔8〕在main.c文件中改写生成第一个用户态进程的函数调用:Spawn_Init_Process(void)

==================user.c===============//产生一个进程〔用户态〕intSpawn(constchar*program,constchar*command,structKernel_Thread**pThread){

//TODO("Spawnaprocessbyreadinganexecutablefromafilesystem");

intrc;//标记各函数的返回值,为0那么表示成功,否那么失败

char*exeFileData=0;//保存在内存缓冲中的用户程序可执行文件

ulong_texeFileLength;//可执行文件的长度

structUser_Context*userContext=0;//指向User_Conetxt的指针

structKernel_Thread*process=0;//指向Kernel_Thread*pThread的指针

structExe_FormatexeFormat;//调用Parse_ELF_Executable函数得到的可执行文件信息

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

{//调用Read_Fully函数将名为program的可执行文件全部读入内存缓冲区

Print("FailedtoReadFile%s!\n",program);

gotofail;

}

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

{//调用Parse_ELF_Executable函数分析ELF格式文件

Print("FailedtoParseELFFile!\n");

gotofail;

}

if((rc=Load_User_Program(exeFileData,exeFileLength,&exeFormat,command,&userContext))!=0)

{//调用Load_User_Program将可执行程序的程序段和数据段装入内存

Print("FailedtoLoadUserProgram!\n");

gotofail;

}

//在堆分配方式下释放内存并再次初始化exeFileData

Free(exeFileData);

exeFileData=0;

/*开始用户进程,调用Start_User_Thread函数创立一个进程并使其进入准备运行队列*/

process=Start_User_Thread(userContext,false);

if(process!=0){//不是核心级进程(即为用户级进程)

KASSERT(process->refCount==2);

/*返回核心进程的指针*/

*pThread=process;

rc=process->pid;//记录当前进程的ID

}else//超出内存project2\include\geekos\errno.h

rc=ENOMEM;

returnrc;

fail://如果新进程创立失败那么注销User_Context对象

if(exeFileData!=0)

Free(exeFileData);//释放内存

if(userContext!=0)

Destroy_User_Context(userContext);//销毁进程对象

returnrc;}//切换至用户上下文voidSwitch_To_User_Context(structKernel_Thread*kthread,structInterrupt_State*state){

//TODO("Switchtoanewuseraddressspace,ifnecessary");

staticstructUser_Context*s_currentUserContext;/*lastusercontextused*/

//externintuserDebug;

structUser_Context*userContext=kthread->userContext;//指向User_Conetxt的指针,并初始化为准备切换的进程

KASSERT(!Interrupts_Enabled());

if(userContext==0){

//userContext为0表示此进程为核心态进程就不用切换地址空间

return;

}

if(userContext!=s_currentUserContext){

ulong_tesp0;

//if(userDebug)Print("A[%p]\n",kthread);

Switch_To_Address_Space(userContext);//为用户态进程时那么切换地址空间

esp0=((ulong_t)kthread->stackPage)+PAGE_SIZE;

//if(userDebug)

//

Print("S[%lx]\n",esp0);

/*新进程的核心栈.*/

Set_Kernel_Stack_Pointer(esp0);//设置内核堆栈指针

/*Newusercontextisactive*/

s_currentUserContext=userContext;

}}

==================

elf.c====================

copyproject1

===================

userseg.c===================//需在此文件各函数前增加一个函数,此函数的功能是按给定的大小创立一个用户级进程上下文,具体实现如下:

//函数功能:按给定的大小创立一个用户级进程上下文

staticstructUser_Context*Create_User_Context(ulong_tsize)

{

structUser_Context*UserContext;

size=Round_Up_To_Page(size);

UserContext=(structUser_Context*)Malloc(sizeof(structUser_Context));

//为用户态进程

if(UserContext!=0)

UserContext->memory=Malloc(size);

//为核心态进程

else

gotofail;

//内存为空

if(0==UserContext->memory)

gotofail;

memset(UserContext->memory,'\0',size);

UserContext->size=size;

//以下为用户态进程创立LDT(段描述符表)

//新建一个LDT描述符

UserContext->ldtDescriptor=Allocate_Segment_Descriptor();

if(0==UserContext->ldtDescriptor)

gotofail;

//初始化段描述符

Init_LDT_Descriptor(UserContext->ldtDescriptor,UserContext->ldt,NUM_USER_LDT_ENTRIES);

//新建一个LDT选择子

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

//新建一个文本段描述符

Init_Code_Segment_Descriptor(

&UserContext->ldt[0],

(ulong_t)UserContext->memory,

size/PAGE_SIZE,

USER_PRIVILEGE

);

//新建一个数据段

Init_Data_Segment_Descriptor(

&UserContext->ldt[1],

(ulong_t)UserContext->memory,

size/PAGE_SIZE,

USER_PRIVILEGE

);

//新建数据段和文本段选择子

UserContext->csSelector=Selector(USER_PRIVILEGE,false,0);

UserContext->dsSelector=Selector(USER_PRIVILEGE,false,1);

//将引用数清0

UserContext->refCount=0;

returnUserContext;

fail:

if(UserContext!=0){

if(UserContext->memory!=0){

Free(UserContext->memory);

}

Free(UserContext);

}

return0;

}//摧毁用户上下文voidDestroy_User_Context(structUser_Context*userContext){

//TODO("DestroyaUser_Context");

//

KASSERT(userContext->refCount==0);

/*Freethecontext'sLDTdescriptor*/

//

Free_Segment_Descriptor(userContext->ldtDescriptor);

/*Freethecontext'smemory*/

//

Disable_Interrupts();

//

Free(userContext->memory);

//

Free(userContext);

//

Enable_Interrupts();

//释放占用的LDT

Free_Segment_Descriptor(userContext->ldtDescriptor);

userContext->ldtDescriptor=0;

//释放内存空间

Free(userContext->memory);

userContext->memory=0;

//释放userContext本身占用的内存

Free(userContext);

userContext=0;

}

intLoad_User_Program(char*exeFileData,ulong_texeFileLength,structExe_Format*exeFormat,constchar*command,

structUser_Context**pUserContext)

{

//TODO("Loadauserexecutableintoausermemoryspaceusingsegmentation");

inti;

ulong_tmaxva=0;//要分配的最大内存空间

unsignednumArgs;//进程数目

ulong_targBlockSize;//参数块的大小

ulong_tsize,argBlockAddr;//参数块地址

structUser_Context*userContext=0;

//计算用户态进程所需的最大内存空间

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

//elf.h

structExe_Segment*segment=&exeFormat->segmentList[i];

ulong_ttopva=segment->startAddress+segment->sizeInMemory;/*FIXME:rangecheck*/

if(topva>maxva)

maxva=topva;

}

Get_Argument_Block_Size(command,&numArgs,&argBlockSize);//获取参数块信息

size=Round_Up_To_Page(maxva)+DEFAULT_USER_STACK_SIZE;//用户进程大小=参数块总大小+进程堆栈大小(8192)

argBlockAddr=size;

size+=argBlockSize;

userContext=Create_User_Context(size);//按相应大小创立一个进程

if(userContext==0)//如果为核心态进程

return-1;

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

structExe_Segment*segment=&exeFormat->segmentList[i];

//根据段信息将用户程序中的各段内容复制到分配的用户内存空间

memcpy(userContext->memory+segment->startAddress,

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

}

//格式化参数块

Format_Argument_Block(userContext->memory+argBlockAddr,numArgs,argBlockAddr,command);

//初始化数据段,堆栈段及代码段信息

userContext->entryAddr=exeFormat->entryAddr;

userContext->argBlockAddr=argBlockAddr;

userContext->stackPointerAddr=argBlockAddr;

//将初始化完毕的User_Context赋给*pUserContext

*pUserContext=userContext;

return0;//成功

}//将用户态的进程复制到内核缓冲区boolCopy_From_User(void*destInKernel,ulong_tsrcInUser,ulong_tbufSize){

//TODO("Copymemoryfromuserbuffertokernelbuffer");

structUser_Context*UserContext=g_currentThread->userContext;

//--:checkifmemoryifvalidated

if(!Validate_User_Memory(UserContext,srcInUser,bufSize))

returnfalse;

//--:user->kernel

memcpy(destInKernel,UserContext->memory+srcInUser,bufSize);

returntrue;

}//将内核态的进程复制到用户态boolCopy_To_User(ulong_tdestInUser,void*srcInKernel,ulong_tbufSize){

//TODO("Copymemoryfromkernelbuffertouserbuffer")

structUser_Context*UserContext=g_currentThread->userContext;

//--:checkifmemoryifvalidated

if(!Validate_User_Memory(UserContext,destInUser,

bufSize))

returnfalse;

//--:kernel->user

memcpy(UserContext->memory+destInUser,srcInKernel,bufSize);

returntrue;

}//切换到用户地址空间voidSwitch_To_Address_Space(structUser_Context*userContext)

{

//TODO("Switchtouseraddressspaceusingsegmentation/LDT");

ushort_tldtSelector=userContext->ldtSelector;/*SwitchtotheLDTofthenewusercontext*/

__asm____volatile__("lldt%0"::"a"(ldtSelector));}

=================

kthread.c===============添加头文件#include<geekos/user.h>//创立一个用户进程/*static*/voidSetup_User_Thread(structKernel_Thread*kthread,structUser_Context*userContext)

{

//TODO("Createanewthreadtoexecuteinusermode");

ulong_teflags=EFLAGS_IF;

unsignedcsSelector=userContext->csSelector;//CS选择子

unsigneddsSelector=userContext->dsSelector;//DS选择子

Attach_User_Context(kthread,userContext);

//初始化用户态进程堆栈,使之看上去像刚被中断运行一样

//分别调用Push函数将以下数据压入堆栈

Push(kthread,dsSelector);

//数据选择子

Push(kthread,userContext->stackPointerAddr);

//堆栈指针

Push(kthread,eflags);

//Eflags

Push(kthread,csSelector);

//文本选择子

Push(kthread,userContext->entryAddr);

//程序计数器

Push(kthread,0);

//错误代码(0)

Push(kthread,0);

//中断号(0)

//初始化通用存放单元,将ESI用户传递参数块地址

Push(kthread,0);/*eax*/

Push(kthread,0);/*ebx*/

Push(kthread,0);/*edx*/

Push(kthread,0);/*edx*/

Push(kthread,userContext->argBlockAddr);/*esi*/

Push(kthread,0);/*edi*/

Push(kthread,0);/*ebp*/

//初始化数据段存放单元

Push(kthread,dsSelector);/*ds*/

Push(kthread,dsSelector);/*es*/

Push(kthread,dsSelector);/*fs*/

Push(kthread,dsSelector);/*gs*/

}

//开始用户进程

structKernel_Thread*Start_User_Thread(structUser_Context*userContext,booldetached)

{

//TODO("Startuserthread");

structKernel_Thread*kthread=Create_Thread(PRIORITY_USER,detached);

//为用户态进程

if(kthread!=0){

Setup_User_Thread(kthread,userContext);

Make_Runnable_Atomic(kthread);

}

returnkthread;}

================

syscall.c=================//需在此文件别的函数前增加一个函数,函数名为Copy_User_String,它被函数Sys_PrintString调用,具体实现如下:

staticintCopy_User_String(ulong_tuaddr,ulong_tlen,ulong_tmaxLen,char**pStr)

{

intrc=0;

char*str;

//超过最大长度

if(len>maxLen){

returnEINVALID;

}

//为字符串分配空间

str=(char*)Malloc(len+1);

if(0==str){

rc=ENOMEM;

gotofail;

}

//从用户空间中复制数据

if(!Copy_From_User(str,uaddr,len)){

rc=EINVALID;

Free(str);

gotofail;

}

str[len]='\0';

//成功

*pStr=str;

fail:

returnrc;

}staticintSys_Exit(structInterrupt_State*state)

{

//TODO("Exitsystemcall");

Exit(state->ebx);}staticintSys_PrintString(structInterrupt_State*state)

{

//TODO("PrintStringsystemcall");

intrc=0;//返回值

uint_tlength=state->ecx;//字符串长度

uchar_t*buf=0;

if(length>0){

/*Copystringintokernel.将字符串复制到内核*/

if((rc=Copy_User_String(state->ebx,length,1023,(char**)&buf))!=0)

gotodone;

/*Writetoconsole.将字符串打印到屏幕*/

Put_Buf(buf,length);

}

done:

if(buf!=0)

Free(buf);

returnrc;

}staticintSys_GetKey(structInterrupt_State*state)

{

//TODO("GetKeysystemcall");

returnWait_For_Key();//返回按键码keyboard.c/Wait_For_Key()}

staticintSys_SetAttr(structInterrupt_State*state)

{

//TODO("SetAttrsystemcall");

Set_Current_Attr((uchar_t)state->ebx);

return0;}staticintSys_GetCursor(structInterrupt_State*state)

{

//TODO("GetCursorsystemcall");

introw,col;

Get_Cursor(&row,&col);

if(!Copy_To_User(state->e

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论