BUAA_OS lab3 難點梳理


BUAA_OS lab3 難點梳理

實驗難點

進程創建

對於初始化部分,首先需要在pmap.c中修改mips_vm_init()函數,為envs開空間,並map到UENVS空間。

其次,模仿page_init()的做法,將空閑進程控制塊串成env_free_list。

至此沒有什么理解上的難度。

 

進程部分的難點,主要在於進程創建流程的理解。進程創建的流程為:

  1. 從env_free_list中獲取一個空的PCB

  2. 初始化進程控制塊

  3. 為進程分配資源

  4. 從空閑鏈表中移出,開始執行

 

STEP1&2

env_alloc()函數清晰地展現了這一進程的前兩步,我們以此展開分析。

 1  int
 2  env_alloc(struct Env **new, u_int parent_id)
 3  {
 4      int r;
 5      struct Env *e;
 6  7      /*Step 1: Get a new Env from env_free_list*/
 8      if(LIST_EMPTY(&env_free_list)){
 9          *new=NULL;
10          return -E_NO_FREE_ENV;
11      }
12      e = LIST_FIRST(&env_free_list);
13 14      /*Step 2: Call certain function(has been completed just now) to init kernel memory layout for this new Env.
15       *The function mainly maps the kernel address to this new Env address. */
16      
17      r = env_setup_vm(e);
18      if(r<0){
19          return r;
20      }
21 22      /*Step 3: Initialize every field of new Env with appropriate values.*/
23      e->env_id=mkenvid(e);
24      e->env_parent_id=parent_id;
25      e->env_status=ENV_RUNNABLE;
26 27      /*Step 4: Focus on initializing the sp register and cp0_status of env_tf field, located at this new Env. */
28      e->env_tf.cp0_status = 0x10001004;
29      e->env_tf.regs[29]=USTACKTOP;
30 31 32      /*Step 5: Remove the new Env from env_free_list. */
33      LIST_REMOVE(e,env_link);
34      *new=e;
35      return 0;
36 37  }
env_alloc

首先,從env_free_list中取出一個空PCB。

然后調用env_setup_vm()函數,該函數的主要作用是初始化新進程的空間。具體實現如下:

 1 static int
 2  env_setup_vm(struct Env *e)
 3  {
 4  //printf("start_env_setup_vm\n");
 5      int i, r;
 6      struct Page *p = NULL;
 7      Pde *pgdir;
 8  9      /* Step 1: Allocate a page for the page directory
10       * using a function you completed in the lab2 and add its pp_ref.
11       * pgdir is the page directory of Env e, assign value for it. */
12      r = page_alloc(&p);
13      if (r < 0) {
14          panic("env_setup_vm - page alloc error\n");
15          return r;
16      }
17      p->pp_ref++;
18      pgdir = (Pde *)page2kva(p);
19      /*Step 2: Zero pgdir's field before UTOP. */
20      for(i=0;i<PDX(UTOP);i++){
21          pgdir[i]=0;
22      }
23      
24      /*Step 3: Copy kernel's boot_pgdir to pgdir. */
25 26      /* Hint:
27       *  The VA space of all envs is identical above UTOP
28       *  (except at UVPT, which we've set below).
29       *  See ./include/mmu.h for layout.
30       *  Can you use boot_pgdir as a template?
31       */
32      for(i=PDX(UTOP);i<=PDX(~0);i++){
33          pgdir[i]=boot_pgdir[i];
34      }
35      e->env_pgdir = pgdir;
36      e->env_cr3 = PADDR(pgdir);
37      // UVPT maps the env's own page table, with read-only permission.
38      e->env_pgdir[PDX(UVPT)]  = e->env_cr3 | PTE_V|PTE_R;
39  //  printf("end_setup_vm\n");
40      return 0;
41  }
env_setup_vm

首先我們要明確,每個進程都有自己的頁表

在這個函數中,首先調用page_alloc()為該進程分配一個頁目錄頁。獲取該頁的虛擬地址為pgdir的虛擬地址(至於為什么是虛擬地址,lab2中已有說明)。

接下來,需要將內核部分的頁表進行拷貝。這是因為每個進程都有自己單獨的頁表,這個頁表會映射完整的4G空間。但由於實驗中采用的是2G+2G的模式,對於所有進程而言,用戶態是不同的,但內核態部分是相同的(共享)。所以,所有進程的頁表的內核2G部分都是完全相同的

完成頁表拷貝之后,需要對PCB中相應值進行設置。然后回到env_alloc()。

接下來需要設置PCB中的某些值,其中尤其要注意的是e->env_tf.cp0_status。該設置使得能正常相應中斷。然后將該進程從空閑列表中移出。

至此,創建進程的前兩步完成。

STEP3&4

創建進程第三步,本質上也就是加載二進制鏡像,在lab3中涉及三個函數,主要步驟如下:

  1. load_icode()函數,初始化一個棧,然后調用load_elf()函數。

  1. load_elf()負責解析ELF文件的字段,並調用load_icode_mapper()函數。

  2. load_icode_mapper()則根據傳入的參數將ELF文件內容加載進內存。

  3. 返回load_icode()函數后,設置pc寄存器值,使得能正常進入執行

這一部分也沒有很復雜的邏輯,但是難在load_icode_mapper()函數的實現。

首先來看一個指導書中的圖,可以說是活命必需品:

難點就在於,需要處理情況種類較多,需要重合考慮va是否對齊;bin_size結尾處是否對齊;sgsize結尾處是否對齊。

 

進程運行和切換

這一部分涉及函數為env_run()。其作用為保存當前進程上下文+恢復啟動進程上下文

 1 void
 2  env_run(struct Env *e)
 3  {
 4      /*Step 1: save register state of curenv. */
 5      /* Hint: if there is an environment running, you should do
 6      *  switch the context and save the registers. You can imitate env_destroy() 's behaviors.*/
 7  //  printf("start run\n");
 8      struct Trapframe *old;
 9      old = (struct Trapframe *)(TIMESTACK - sizeof(struct Trapframe));
10 11      if(curenv!=NULL){
12          curenv->env_tf=*old;
13          curenv->env_tf.pc=curenv->env_tf.cp0_epc;
14      }
15 16      /*Step 2: Set 'curenv' to the new environment. */
17      //printf("start curenv=e\n");
18      curenv=e;
19      curenv->env_status=ENV_RUNNABLE;
20      /*Step 3: Use lcontext() to switch to its address space. */
21  //  printf("start lcontext\n");
22      lcontext((int)e->env_pgdir);
23      
24      /*Step 4: Use env_pop_tf() to restore the environment's
25       * environment   registers and return to user mode.
26       *
27       * Hint: You should use GET_ENV_ASID there. Think why?
28       * (read <see mips run linux>, page 135-144)
29       */
30  //  printf("start pop tf\n");
31      env_pop_tf(&curenv->env_tf, GET_ENV_ASID(curenv->env_id));
32      printf("end run\n");
33  }
env_run

首先,我們取出old,及當前環境上下文(寄存器的值等)。

然后將當前環境保存進當前進程的env_tf中,並當前進程的pc設置為cp0_epc,讓其陷入中斷。

到這里,保存現場的任務完成,可以將curenv設置為下一進程e。

最后,調用env_pop_tf()恢復現場。

在進程切換過程中,最難理解的就是TIMESTACK的含義。我認為TIMESTACK是時鍾棧,存儲時鍾中斷的時候存的trapframe。進入時鍾中斷后,把TIMESTACK的值賦值給寄存器們,再執行中斷處理。而KERNEL_SP應當是系統調用后的存儲區。

有關TIMESTACK,還有個很難理解的地方,在以下函數中:

 

 void
 env_destroy(struct Env *e)
 {
     /* Hint: free e. */
     env_free(e);
 ​
     /* Hint: schedule to run a new environment. */
     if (curenv == e) {
         curenv = NULL;
         /* Hint:Why this? */
         bcopy((void *)KERNEL_SP - sizeof(struct Trapframe),
               (void *)TIMESTACK - sizeof(struct Trapframe),
               sizeof(struct Trapframe));
         printf("i am killed ... \n");
         sched_yield();
     }
 }

 

為什么要在destroy進程的時候,將KERNEL_SP的tf拷貝到TIMESTACK中?百思不得其解。

個人想法是,在調用sched_yield()獲取下一個要執行的進程之前,要將環境恢復到調用當前進程之前的環境。

也可能和kill到最后一個進程的時候要恢復到最初狀態有關。

 

中斷異常

中斷一場部分代碼量較小,主要需要理解的是遇到中斷異常后函數的調用關系。

  1. 跳轉到.text.exc_vec3代碼段

  2. 根據時鍾中斷,分發handle_ int函數來處理時鍾中斷

  3. timer_ irq 里跳轉到sched_ yield,選擇下一個進程執行。

調度函數的實現根據注釋來也沒有大問題。但是在后期lab4-extra的時候可能會由於調度錯誤導致無法通過,所以需要盡量保證情況周全。

 

(代碼倉庫位於右上角Github)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM