1、计算机操作系统实验运行用户态程序西北工业大学 操作系统实验 实验报告一、实验目的掌握在GeekOS系统用户态模式下加载并运行可执行程序的方法。二、实验要求1. 按照实验讲义P127页中的设计要求,实现在用户态模式下加载并运行可执行程序的代码,给出关键函数的代码以及实验结果。三、实验过程及结果答:核心函数代码如下:= user.c =/产生一个进程(用户态)int Spawn(const char *program, const char *command, struct Kernel_Thread *pThread) /TODO(Spawn a process by reading an ex
2、ecutable 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);
3、 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; /在堆分配方式下释放内存并再次初始化exeF
4、ileData 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 rc = ENOMEM; return rc; fail: /如果新进程创建
5、失败则注销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 us
6、er 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_Addr
7、ess_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 *exeFileDa
8、ta, 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-num
9、Segments=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-
10、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 Us
11、er_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 = A
12、llocate_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); /新建一个文本段
13、描述符 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_PRIV
14、ILEGE, 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* u
15、serContext) /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 exeFil
16、eLength,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 Use
17、r_Context *userContext = 0; /计算用户态进程所需的最大内存空间 for (i = 0; i numSegments; +i) /elf.h struct 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,
18、&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-segmentLi
19、sti; /根据段信息将用户程序中的各段内容复制到分配的用户内存空间 memcpy(userContext-memory + segment-startAddress, exeFileData + segment-offsetInFile,segment-lengthInFile); /格式化参数块 Format_Argument_Block(userContext-memory + argBlockAddr, numArgs, argBlockAddr, command); /初始化数据段,堆栈段及代码段信息 userContext-entryAddr = exeFormat-entryAd
20、dr; 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_cu
21、rrentThread-userContext; /-: check if memory if validated if (!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
22、 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 ldtSelect
23、or= 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 csSelecto
24、r=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(kthrea
25、d, 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); /
26、* 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
27、, 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_Str
28、ing(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* st
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1