桂林电子科技大学综合设计说明书用纸 第 14 页
(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 =============== //产生一个进程(用户态)
int Spawn(const char *program, const char *command, struct Kernel_Thread **pThread) {
//TODO(\ int rc; //标记各函数的返回值,为0则表示成功,否则失败
char *exeFileData = 0;//保存在内存缓冲中的用户程序可执行文件 ulong_t exeFileLength;//可执行文件的长度
struct User_Context *userContext = 0;//指向User_Conetxt的指针
struct Kernel_Thread *process = 0;//指向Kernel_Thread *pThread的指针
struct Exe_Format exeFormat;//调用Parse_ELF_Executable函数得到的可执行文件信息 if ((rc = Read_Fully(program, (void**) &exeFileData, &exeFileLength)) != 0 ) {//调用Read_Fully函数将名为program的可执行文件全部读入内存缓冲区 Print(\ goto fail; }
if((rc = Parse_ELF_Executable(exeFileData, exeFileLength, &exeFormat)) != 0 ) {//调用Parse_ELF_Executable函数分析ELF格式文件
Print(\ goto fail; }
if((rc = Load_User_Program(exeFileData, exeFileLength, &exeFormat, command, &userContext)) != 0) {//调用Load_User_Program将可执行程序的程序段和数据段装入内存 Print(\ goto fail; }
//在堆分配方式下释放内存并再次初始化exeFileData
Free(exeFileData); exeFileData = 0;
/* 开始用户进程,调用Start_User_Thread函数创建一个进程并使其进入准备运行队列*/ process = Start_User_Thread(userContext, false); if (process != 0) { //不是核心级进程(即为用户级进程)
桂林电子科技大学综合设计说明书用纸 第 15 页
KASSERT(process->refCount == 2); /* 返回核心进程的指针 */
*pThread = process;
rc = process->pid;//记录当前进程的ID
} else//超出内存 project2\\include\\geekos\\errno.h rc = 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) {
//TODO(\
static struct User_Context* s_currentUserContext; /* last user context used */ //extern int userDebug;
struct User_Context* userContext = kthread->userContext;//指向User_Conetxt的指针,并初始化为准备切换的进程
KASSERT(!Interrupts_Enabled());
if (userContext == 0) { //userContext为0表示此进程为核心态进程就不用切换地址空间 return; }
if (userContext != s_currentUserContext) { ulong_t esp0;
//if (userDebug) Print(\
Switch_To_Address_Space(userContext);//为用户态进程时则切换地址空间 esp0 = ((ulong_t) kthread->stackPage) + PAGE_SIZE; //if (userDebug)
// Print(\/* 新进程的核心栈. */
Set_Kernel_Stack_Pointer(esp0);//设置内核堆栈指针
/* New user context is active */
s_currentUserContext = userContext; } }
================== elf.c ==================== copy project1
=================== userseg.c =================== //需在此文件各函数前增加一个函数,此函数的功能是按给定的大小创建一个用户级进程上下文,具体实现如下:
桂林电子科技大学综合设计说明书用纸 第 16 页
//函数功能:按给定的大小创建一个用户级进程上下文
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->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
桂林电子科技大学综合设计说明书用纸 第 17 页
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(\// KASSERT(userContext->refCount == 0); /* Free the context's LDT descriptor */
// Free_Segment_Descriptor(userContext->ldtDescriptor); /* Free the context's memory */ // 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; }
----------------------------------------------
int Load_User_Program(char *exeFileData, ulong_t exeFileLength,struct Exe_Format *exeFormat, const char *command,
struct User_Context **pUserContext)
{ //TODO(\ int i;
ulong_t maxva = 0;//要分配的最大内存空间 unsigned numArgs;//进程数目
ulong_t argBlockSize;//参数块的大小 ulong_t size, argBlockAddr;//参数块地址 struct User_Context *userContext = 0;
桂林电子科技大学综合设计说明书用纸 第 18 页
//计算用户态进程所需的最大内存空间
for (i = 0; i < exeFormat->numSegments; ++i) { //elf.h
struct Exe_Segment *segment = &exeFormat->segmentList[i];
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;//用户进程大小=参数块总大小 + 进程堆栈大小(8192)
argBlockAddr = size; size += argBlockSize;
userContext = Create_User_Context(size);//按相应大小创建一个进程 if (userContext == 0)//如果为核心态进程 return -1;
for (i = 0; i < exeFormat->numSegments; ++i) {
struct Exe_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; return 0;//成功
}
---------------------------------------------- //将用户态的进程复制到内核缓冲区
bool Copy_From_User(void* destInKernel, ulong_t srcInUser, ulong_t bufSize) { //TODO(\
struct User_Context * UserContext = g_currentThread->userContext; //--: check if memory if validated
if (!Validate_User_Memory(UserContext,srcInUser, bufSize)) return false; //--:user->kernel
memcpy(destInKernel, UserContext->memory + srcInUser, bufSize); return true; }
----------------------------------------- //将内核态的进程复制到用户态