Demo entry 6682482

wfwef

   

Submitted by anonymous on Dec 09, 2017 at 02:09
Language: C. Code size: 12.5 kB.

Use_To_Kernel() :user态的程序的地址空间转换为内核态的地址空间,内核地址实际上就是用户线程的基址加偏移。
static void * User_To_Kernel(Sturct User_Context* current, ulong_t srcInUser){
	uchar_t * userBase = (uchar_t*) current->memory;
	return (void *)(UserBase + srcInUser);
}


int Spawn(const char *program, const char *command, struct Kernel_Thread **pThread)
{	
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("Failed to Read File %s!\n", program);
  goto fail;
 }
 if((rc = Parse_ELF_Executable(exeFileData, exeFileLength, &exeFormat)) != 0 )
 {//调用Parse_ELF_Executable函数分析ELF格式文件
  Print("Failed to Parse ELF File!\n");
        goto fail;
 }
 if((rc = Load_User_Program(exeFileData, exeFileLength, &exeFormat, command, &userContext)) != 0)
 {//调用Load_User_Program将可执行程序的程序段和数据段装入内存
  Print("Failed to Load User Program!\n");
  goto fail;
 }
//在堆分配方式下释放内存并再次初始化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;
  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("Switch to a new user address space, if necessary");
 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("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);//设置内核堆栈指针
/* New user context is active */
  s_currentUserContext = userContext;
  }
} 
			

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
    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 < 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("Copy memory from user buffer to kernel buffer");
 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;
}


//将内核态的进程复制到用户态
bool Copy_To_User(ulong_t destInUser, void* srcInKernel, ulong_t bufSize)
{      //TODO("Copy memory from kernel buffer to user buffer")
 struct User_Context * UserContext = g_currentThread->userContext;
 //--: check if memory if validated
 if (!Validate_User_Memory(UserContext, destInUser,  bufSize))
  return false;
 
 //--:kernel->user
 memcpy(UserContext->memory + destInUser, srcInKernel, bufSize);
 return true;
}

//切换到用户地址空间

void Switch_To_Address_Space(struct User_Context *userContext)
{    //TODO("Switch to user address space using segmentation/LDT");
 ushort_t ldtSelector= userContext->ldtSelector;/* Switch to the LDT of the new user context */
 __asm__ __volatile__ ("lldt %0"::"a"(ldtSelector));
}


void Setup_User_Thread(struct Kernel_Thread* kthread, struct User_Context* userContext)
{    //TODO("Create a new thread to execute in user mode");
    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)
{   //TODO("Start user thread");
 struct Kernel_Thread* kthread = Create_Thread(PRIORITY_USER, detached);
 //为用户态进程
 if (kthread != 0){
  Setup_User_Thread(kthread, userContext);
  Make_Runnable_Atomic(kthread);
 }
 return kthread;
}


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;
    }
    str[len] = '\0';
    //成功
    *pStr = str;
fail:
    return rc;
}


static int Sys_Exit(struct Interrupt_State* state)
{    //TODO("Exit system call");
 Exit(state->ebx);
}


static int Sys_PrintString(struct Interrupt_State* state)
{    //TODO("PrintString system call");
 int rc = 0;//返回值
 uint_t length = state->ecx;//字符串长度
 uchar_t* buf = 0;
 if (length > 0) {
/* Copy string into kernel. 将字符串复制到内核*/
  if ((rc = Copy_User_String(state->ebx, length, 1023, (char**) &buf)) != 0)
   goto done;
/* Write to console. 将字符串打印到屏幕 */
  Put_Buf(buf, length);
  }
done:
  if (buf != 0)
   Free(buf);
  return rc;
}	

static int Sys_GetKey(struct Interrupt_State* state)
{    //TODO("GetKey system call");
 return Wait_For_Key(); //返回按键码keyboard.c/Wait_For_Key()
}

static int Sys_SetAttr(struct Interrupt_State* state)
{    //TODO("SetAttr system call");
 Set_Current_Attr((uchar_t) state->ebx);
 return 0;
}
static int Sys_GetCursor(struct Interrupt_State* state)
{    //TODO("GetCursor system call");
 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)
{    //TODO("PutCursor system call");
 return Put_Cursor(state->ebx, state->ecx) ? 0 : -1;
}

static int Sys_Spawn(struct Interrupt_State* state)
{    //TODO("Spawn system call");
    int rc;                 //函数返回值
    char *program = 0;      //进程名称
    char *command = 0;      //用户命令
 struct Kernel_Thread *process;
/* Copy program name and command from user space. */
 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;
}


static void Spawn_Init_Process(void)
{    //TODO("Spawn the init process");
    struct Kernel_Thread *pThread;
    Spawn("/c/shell.exe","/c/shell.exe",&pThread);
}

This snippet took 0.02 seconds to highlight.

Back to the Entry List or Home.

Delete this entry (admin only).