iT邦幫忙

0

[6.1810][code] xv6 的 Processes (六)

  • 分享至 

  • xImage
  •  

系列文章 : [6.1810] 跟著 MIT 6.1810 學習基礎作業系統觀念

大綱

  • kernel/proc.c/reparent
  • kernel/proc.c/forkret
  • kernel/proc.c/setkilled
  • kernel/proc.c/killed
  • kernel/proc.c/either_copyout
  • kernel/proc.c/either_copyin
  • kernel/proc.c/procdump

kernel/proc.c/reparent

// Pass p's abandoned children to init.
// Caller must hold wait_lock.
void
reparent(struct proc *p)
{
  struct proc *pp;

  for(pp = proc; pp < &proc[NPROC]; pp++){
    if(pp->parent == p){
      pp->parent = initproc;
      wakeup(initproc);
    }
  }
}
  • 遍尋整個 global-process-table
  • 找出 struct proc *p 所有的 children-process
  • 把這些 children-process 的 parent 通通換成特殊的 initproc
  • 把 initproc 喚醒,嘗試讓 initproc 透過 wait 去回收 children-process 的資源 ( 假如 children-process exit 的話 )
  • 這個 function 通常在 struct proc *p 要呼叫 kexit 結束執行的時候,會去呼叫。因為每個 process 都需要有 parent,當原本的 parent 結束自己的生命週期,停止執行的時候,需要讓 initproc 成為新的 parent。


kernel/proc.c/forkret

// A fork child's very first scheduling by scheduler()
// will swtch to forkret.
void
forkret(void)
{
  • forkret 是所有新創造的 process 首先會執行的一個 kernel space function。因為在 allocproc function 這邊,會將 p->context.ra = (uint64)forkret;。在 CPU-scheduler-thread 挑選到這個 struct-proc 的時候,會去執行 forkret
  • 而新創造的 process 首先會執行的第一個 user space function 會是 parent process 的下一道 instruction,因為 kfork 會把 parent process 的 struct-proc->trapframe 複製給 child process,而 trapframe 裡面包含 epc,也就是從 kernel space return 回 user space 的時候,Program Counter 會被設定的值。

  extern char userret[];
  static int first = 1;
  struct proc *p = myproc();

  // Still holding p->lock from scheduler.
  release(&p->lock);
  • 在 CPU-scheduler-thread 挑選中這個 process 的時候,也會拿取該 process 的 struct-proc->lock,這邊將它解鎖。

  if (first) {
    // File system initialization must be run in the context of a
    // regular process (e.g., because it calls sleep), and thus cannot
    // be run from main().
    fsinit(ROOTDEV);

    first = 0;
    // ensure other cores see first=0.
    __sync_synchronize();

    // We can invoke kexec() now that file system is initialized.
    // Put the return value (argc) of kexec into a0.
    p->trapframe->a0 = kexec("/init", (char *[]){ "/init", 0 });
    if (p->trapframe->a0 == -1) {
      panic("exec");
    }
  }
  • if (first) {
    • first == true 的話,表示這是第一個被創建的 user process。
  • fsinit(ROOTDEV);
    • 對 filesystem 進行初始化。
    • 因為過程中會呼叫 sleep,所以需要在一般的 process 下進行初始化,而這個 initproc 正是開機以來的第一個 process。
  • p->trapframe->a0 = kexec("/init", (char *[]){ "/init", 0 });
    • 執行到這裡,表示 file system 已經初始化完畢了 ( fsinit ),所以可以使用 kexec。
    • kexec 會將 struct-proc->trapframe->epc 設定為 /init 這個 ELF 執行檔的 entry point,於是在該 process 從 kernel space 返回 user space 的時候,program counter 會跳轉到 /init 這個 ELF 執行檔的 main function。
  • if (p->trapframe->a0 == -1) {
    • kexec 回傳 -1 的話,表示 kexec 執行失敗了。

  // return to user space, mimicing usertrap()'s return.
  prepare_return();
  • prepare_return();
    • 這個 function 會把一些資訊存回 p->trapframe,以及設定當前 CPU 的一些 CSRs,為返回 user space 來作準備。
    • trapframe
      • kernel_satp
      • kernel_sp
      • kernel_trap
      • kernel_hartid
    • CSRs
      • stvec
      • sstatus.SPP
      • sstatus.SPIE
      • sepc

  uint64 satp = MAKE_SATP(p->pagetable);
  uint64 trampoline_userret = TRAMPOLINE + (userret - trampoline);
  ((void (*)(uint64))trampoline_userret)(satp);
}
  • uint64 satp = MAKE_SATP(p->pagetable);
    • 將 struct-proc 的 pagetable 指標透過 MAKE_SATP,將 pagetabe 指標轉換成 satp register 的格式
  • uint64 trampoline_userret = TRAMPOLINE + (userret - trampoline);
    • userrest : kernel/trampoline.S/userret 的 physical address
    • trampoline : kernel/trampoline.S/trampoline 的 physical address
    • TRAMPOLINE : trampoline 的 virtual address
    • 這一行程式碼取得 userret 的 virtual address
  • ((void (*)(uint64))trampoline_userret)(satp);
    • 以 satp 為第 0 個參數,去呼叫 kernel/trampoline.S/userret 這個 function。


kernel/proc.c/setkilled

void
setkilled(struct proc *p)
{
  acquire(&p->lock);
  p->killed = 1;
  release(&p->lock);
}
  • 將一個 struct-proc 設為被 kill 的狀態
  • 被 kill 的 process,會在 kernel/trap.c/usertrap 真的去呼叫 kexit 去結束掉自己的生命。


kernel/proc.c/killed

int
killed(struct proc *p)
{
  int k;
  
  acquire(&p->lock);
  k = p->killed;
  release(&p->lock);
  return k;
}

去判斷一個 process 是否已經被 kill 掉了。



kernel/proc.c/either_copyout

// Copy to either a user address, or kernel address,
// depending on usr_dst.
// Returns 0 on success, -1 on error.
int
either_copyout(int user_dst, uint64 dst, void *src, uint64 len)
{
  • this function
    • 這個 function 可以讓 kernel 複製資料到 user space 或是 kernel space
    • 也就是說資料的目的地 ( destination ) 可以是 user space 或是 kernel space
  • (arg) int user_dst
    • 這是一個 boolean flag
    • non-zero ( true ) : destination 是 user space address
    • zero ( false ) : destination 是 kernel space address
  • (arg) uint64 dst
    • destination address。
  • (arg) void *src
    • source address,必定會是 kernel space address。
  • (arg) uint64 len
    • 想要複製多少長度的資料。
  • return value
    • 0 : 代表成功
    • -1 : 代表失敗

  struct proc *p = myproc();
  if(user_dst){
    return copyout(p->pagetable, dst, src, len);
  } else {
    memmove((char *)dst, src, len);
    return 0;
  }
}
  • 假如 destination 是 user space,則呼叫 copyout 處理這次的請求
  • 假如 destination 是 kernel space,則只需要簡單的 memmove


kernel/proc.c/either_copyin

// Copy from either a user address, or kernel address,
// depending on usr_src.
// Returns 0 on success, -1 on error.
int
either_copyin(void *dst, int user_src, uint64 src, uint64 len)
{
  • this function
    • 這個 function 可以讓 kernel 從 user space 或是 kernel space 複製資料到 kernel space。
    • 也就是說,資料的來源 ( source ) 可以是 user space 或是 kernel space
  • (arg) void *dst
    • destination address,必定會在 kernel space。
  • (arg) int user_src
    • 這是一個 boolean flag
    • non-zero ( true ) : source 是 user space address
    • zero ( false ) : source 是 kernel space address
  • (arg) uint64 src
    • source address。
  • (arg) uint64 len
    • 想要複製多少長度的資料。
  • return value
    • 0 : 代表成功
    • -1 : 代表失敗

  struct proc *p = myproc();
  if(user_src){
    return copyin(p->pagetable, dst, src, len);
  } else {
    memmove(dst, (char*)src, len);
    return 0;
  }
}
  • 假如 source 是 user space,則呼叫 copyin 處理這次的請求
  • 假如 source 是 kernel space,則只需要簡單的 memmove


kernel/proc.c/procdump

// Print a process listing to console.  For debugging.
// Runs when user types ^P on console.
// No lock to avoid wedging a stuck machine further.
void
procdump(void)
{
  static char *states[] = {
  [UNUSED]    "unused",
  [USED]      "used",
  [SLEEPING]  "sleep ",
  [RUNNABLE]  "runble",
  [RUNNING]   "run   ",
  [ZOMBIE]    "zombie"
  };
  struct proc *p;
  char *state;

  printf("\n");
  for(p = proc; p < &proc[NPROC]; p++){
    if(p->state == UNUSED)
      continue;
    if(p->state >= 0 && p->state < NELEM(states) && states[p->state])
      state = states[p->state];
    else
      state = "???";
    printf("%d %s %s", p->pid, state, p->name);
    printf("\n");
  }
}
  • 可以印出所有非 UNUSED 的 process 的資訊,是個方便 debug 的好工具。
  • 當我們在 xv6-riscv 的 shell 按一下 ctrl+p,就可以執行這個 function 來 dump 所有 struct-proc 的資訊



圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言