计算机操作系统实验-运行用户态程序.doc

上传人:xin****828 文档编号:6665197 上传时间:2020-03-01 格式:DOC 页数:13 大小:210KB
返回 下载 相关 举报
计算机操作系统实验-运行用户态程序.doc_第1页
第1页 / 共13页
计算机操作系统实验-运行用户态程序.doc_第2页
第2页 / 共13页
计算机操作系统实验-运行用户态程序.doc_第3页
第3页 / 共13页
点击查看更多>>
资源描述
西北工业大学 操作系统实验 实验报告一、实验目的掌握在GeekOS系统用户态模式下加载并运行可执行程序的方法。二、实验要求1. 按照实验讲义P127页中的设计要求,实现在用户态模式下加载并运行可执行程序的代码,给出关键函数的代码以及实验结果。三、实验过程及结果答:核心函数代码如下:= user.c =/产生一个进程(用户态)int Spawn(const char *program, const char *command, struct Kernel_Thread *pThread)/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 Kernel_Thread *process = 0;struct Exe_Format exeFormat;if (rc = Read_Fully(program, (void*) &exeFileData, &exeFileLength) != 0 )Print(Failed 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;/在堆分配方式下释放内存并再次初始化exeFileDataFree(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 elserc = ENOMEM;return rc;fail: /如果新进程创建失败则注销User_Context对象if (exeFileData != 0)Free(exeFileData);/释放内存if (userContext != 0)Destroy_User_Context(userContext);/销毁进程对象return rc;-/切换至用户上下文void Switch_To_User_Context(struct Kernel_Thread* kthread, struct Interrupt_State* state)static struct User_Context* s_currentUserContext; /* last user context used */extern int userDebug;struct User_Context* userContext = kthread-userContext;KASSERT(!Interrupts_Enabled();if (userContext = 0) /userContext为0表示此进程为核心态进程就不用切换地址空间return;if (userContext != s_currentUserContext) ulong_t esp0;/if (userDebug) Print(A%pn, kthread);Switch_To_Address_Space(userContext);/为用户态进程时则切换地址空间esp0 = (ulong_t) kthread-stackPage) + PAGE_SIZE;/if (userDebug)/ Print(S%lxn, esp0);/* 新进程的核心栈. */Set_Kernel_Stack_Pointer(esp0);/设置内核堆栈指针/* New user context is active */s_currentUserContext = userContext; = elf.c =int Parse_ELF_Executable(char *exeFileData, ulong_t exeFileLength, struct Exe_Format *exeFormat)int i;elfHeader *head=(elfHeader*)exeFileData;programHeader *proHeader=(programHeader *)(exeFileData+head-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;exeFormat-segmentListi.lengthInFile=proHeader-fileSize;exeFormat-segmentListi.startAddress=proHeader-vaddr;exeFormat-segmentListi.sizeInMemory=proHeader-memSize;exeFormat-segmentListi.protFlags=proHeader-flags;proHeader+;return 0;= userseg.c =/需在此文件各函数前增加一个函数,此函数的功能是按给定的大小创建一个用户级进程上下文,具体实现如下:/函数功能:按给定的大小创建一个用户级进程上下文static struct User_Context* Create_User_Context(ulong_t size) struct User_Context * UserContext; size = Round_Up_To_Page(size); UserContext = (struct User_Context *)Malloc(sizeof(struct User_Context); if (UserContext != 0) UserContext-memory = Malloc(size); /为核心态进程 else goto fail; /内存为空 if (0 = UserContext-memory) goto fail; memset(UserContext-memory, 0, size); UserContext-size = size; /以下为用户态进程创建LDT(段描述符表) /新建一个LDT描述符 UserContext-ldtDescriptor = Allocate_Segment_Descriptor(); if (0 = UserContext-ldtDescriptor) goto fail; /初始化段描述符 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-ldt0, (ulong_t) UserContext-memory, size / PAGE_SIZE, USER_PRIVILEGE ); /新建一个数据段 Init_Data_Segment_Descriptor( &UserContext-ldt1, (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; return UserContext;fail: if (UserContext != 0) if (UserContext-memory != 0) Free(UserContext-memory); Free(UserContext); return 0;-/摧毁用户上下文void Destroy_User_Context(struct User_Context* userContext) /TODO(Destroy a User_Context); /释放占用的LDT Free_Segment_Descriptor(userContext-ldtDescriptor); userContext-ldtDescriptor=0; /释放内存空间 Free(userContext-memory); userContext-memory=0; /释放userContext本身占用的内存 Free(userContext); userContext=0;-int Load_User_Program(char *exeFileData,ulong_t exeFileLength,struct Exe_Format *exeFormat,const char *command,struct User_Context *pUserContext) /TODO(Load a user executable into a user memory space using segmentation);int i;ulong_t maxva = 0;/要分配的最大内存空间unsigned numArgs;/进程数目ulong_t argBlockSize;/参数块的大小ulong_t size, argBlockAddr;/参数块地址struct User_Context *userContext = 0;/计算用户态进程所需的最大内存空间for (i = 0; i numSegments; +i) /elf.hstruct Exe_Segment *segment = &exeFormat-segmentListi;ulong_t topva = segment-startAddress + segment-sizeInMemory; /* FIXME: range check */if (topva maxva)maxva = topva;Get_Argument_Block_Size(command, &numArgs, &argBlockSize);/获取参数块信息size = Round_Up_To_Page(maxva) + DEFAULT_USER_STACK_SIZE;argBlockAddr = size;size += argBlockSize;userContext = Create_User_Context(size);/按相应大小创建一个进程if (userContext = 0)/如果为核心态进程return -1;for (i = 0; i numSegments; +i) struct Exe_Segment *segment = &exeFormat-segmentListi;/根据段信息将用户程序中的各段内容复制到分配的用户内存空间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;return 0;/成功-/将用户态的进程复制到内核缓冲区bool Copy_From_User(void* destInKernel, ulong_t srcInUser, ulong_t bufSize) struct User_Context * UserContext = g_currentThread-userContext;/-: check if memory if validatedif (!Validate_User_Memory(UserContext,srcInUser, bufSize)return false;memcpy(destInKernel, UserContext-memory + srcInUser, bufSize); return true;-/将内核态的进程复制到用户态bool Copy_To_User(ulong_t destInUser, void* srcInKernel, ulong_t bufSize)struct User_Context * UserContext = g_currentThread-userContext;if (!Validate_User_Memory(UserContext, destInUser, bufSize)return false;memcpy(UserContext-memory + destInUser, srcInKernel, bufSize);return true;-/切换到用户地址空间void Switch_To_Address_Space(struct User_Context *userContext) ushort_t ldtSelector= userContext-ldtSelector;/* Switch to the LDT of the new user context */_asm_ _volatile_ (lldt %0:a(ldtSelector);= kthread.c =添加头文件 #include -/创建一个用户进程/*static*/ void Setup_User_Thread(struct Kernel_Thread* kthread, struct User_Context* userContext) ulong_t eflags = EFLAGS_IF; unsigned csSelector=userContext-csSelector;/CS选择子 unsigned dsSelector=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 */ /开始用户进程struct Kernel_Thread* Start_User_Thread(struct User_Context* userContext, bool detached)struct Kernel_Thread* kthread = Create_Thread(PRIORITY_USER, detached);if (kthread != 0)Setup_User_Thread(kthread, userContext);Make_Runnable_Atomic(kthread);return kthread; = syscall.c =/需在此文件别的函数前增加一个函数,函数名为Copy_User_String,它被函数Sys_PrintString调用,具体实现如下:static int Copy_User_String(ulong_t uaddr, ulong_t len, ulong_t maxLen, char *pStr) int rc = 0; char *str; if (len maxLen) /超过最大长度 return EINVALID; str = (char*) Malloc(len+1); /为字符串分配空间 if (0 = str) rc = ENOMEM; goto fail; if (!Copy_From_User(str, uaddr, len) /从用户空间中复制数据 rc = EINVALID; Free(str); goto fail; strlen = 0; /成功 *pStr = str;fail: return rc;-static int Sys_Exit(struct Interrupt_State* state)Exit(state-ebx);-static int Sys_PrintString(struct Interrupt_State* state)int rc = 0;/返回值uint_t length = state-ecx;/字符串长度uchar_t* buf = 0;if (length 0) if (rc = Copy_User_String(state-ebx, length, 1023, (char*) &buf) != 0)goto done;Put_Buf(buf, length);done:if (buf != 0)Free(buf);return rc;-static int Sys_GetKey(struct Interrupt_State* state) return Wait_For_Key(); /返回按键码keyboard.c/Wait_For_Key()-static int Sys_SetAttr(struct Interrupt_State* state) Set_Current_Attr(uchar_t) state-ebx);return 0;-static int Sys_GetCursor(struct Interrupt_State* state) int row, col;Get_Cursor(&row, &col);if (!Copy_To_User(state-ebx, &row, sizeof(int) |!Copy_To_User(state-ecx, &col, sizeof(int)return -1;return 0;-static int Sys_PutCursor(struct Interrupt_State* state) return Put_Cursor(state-ebx, state-ecx) ? 0 : -1;-static int Sys_Spawn(struct Interrupt_State* state) int rc; /函数返回值 char *program = 0; /进程名称 char *command = 0; /用户命令struct Kernel_Thread *process;if (rc = Copy_User_String(state-ebx, state-ecx, VFS_MAX_PATH_LEN, &program) != 0)goto fail;if(rc = Copy_User_String(state-edx, state-esi, 1023, &command) != 0)/从用户空间复制用户命令goto fail;Enable_Interrupts(); /开中断rc = Spawn(program, command, &process);/得到进程名称和用户命令后便可生成一个新进程if (rc = 0) /若成功则返回新进程ID号KASSERT(process != 0);rc = process-pid;Disable_Interrupts();/关中断fail:/返回小于0的错误代码if (program != 0)Free(program);if (command != 0)Free(command);return rc;-static int Sys_Wait(struct Interrupt_State* state) /TODO(Wait system call);int exitCode;struct Kernel_Thread *kthread = Lookup_Thread(state-ebx);if (kthread = 0)return -12;Enable_Interrupts();exitCode = Join(kthread);Disable_Interrupts();return exitCode;-static int Sys_GetPID(struct Interrupt_State* state) /TODO(GetPID system call);return g_currentThread-pid;= main.c =static void Spawn_Init_Process(void) /TODO(Spawn the init process); struct Kernel_Thread *pThread; Spawn(/c/shell.exe,/c/shell.exe,&pThread);实验结果如图所示:原理:Geekos 提供了一个简单的shell,保存在PFAT文件系统内,所以geekos系统启动后,启动shell程序/c/shell.exe运行,将/c/shell.exe作为可执行文件传递给spawn函数的program参数,创建第一个用户态进程,然后由它来创建其他进程。运行后,geekos就可以挂在这shell,并能运行测试文件c.exe和b.exe。四、实验分析GeekOS系统最早创建的内核进程有Idle、Reaper和Main三个进程,它们由Init_Scheduler函数创建:最先初始化一个核态进程mainThread,并将该进程作为当前运行进程,函数最后还调用Start_Kernel_Thread 函数创建了两个系统进程Idle和Reaper。 所以,Idle、Reaper和Main三个进程是系统中最早存在的进程。在GeekOS中为了区分用户态进程和内核进程,在Kernel_Thread结构体中设置了一个字段 userContext,指向用户态进程上下文。对于内核进程来说,这个指针为空,而用户态进程都拥有自己的用户上下文(User_Context)。因此,在GeekOS中要判断一个进程是内核进程还是用户态进程,只要通过userContext字段是否为空来判断就可以了。每个用户态进程都拥有属于自己的内存段空间,如:代码段、数据段、堆栈段等,每个段有一个段描述符(segment descriptor),并且每个进程有一个段描述符表(Local Descriptor Table),用于保存该进程的所有段描述符。操作系统中还设置一个全局描述符表(GDT,Global Descriptor Table),用于记录了系统中所有进程的ldt描述符。用户态进程创建LDT的步骤:(1)调用函数Allocate_Segment_Descriptor()新建一个LDT描述符;(2)调用函数Selector()新建一个LDT选择子;(3)调用函数Init_Code_Segment_Descriptor()新建一个文本段描述符;(4)调用函数Init_Data_Segment_Descriptor()新建一个数据段;(5)调用函数Selector()新建一个数据段选择子;(6)调用函数Selector()新建一个文本(可执行代码)段选择子。用户态进程创建流程:五、所遇问题及解决方法答:对于project 2,更要求读懂代码。虽说网上有很多现成的答案,但不免有出入,不能直接拿来用。还得要求自己去读懂代码,并根据实际情况作出适当的修改。而在读懂代码过程中,仍免不了有各种各样的疑惑。比如Spawn_Program函数,该函数在main.c文件里被调用,作用是导入用户程序并初始化。但当时就有疑惑:main 函数只是直接调用了shell.exe,那其他执行文件又该在哪里调用?后来经过查询,知道Syscall.c文件里有个int_Spawn函数,该函数从空间复制进程名称及用户命令,生成新进程,并调用Spawn_Program函数,其余过程与shell.exe一样。通过这个实验,我对操作系统也有了更深的了解,对操作系统运行用户态程序也有了更深的认识,受益匪浅。
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 临时分类 > 人文社科


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!