1. 程式人生 > 其它 >MIT6.S081 ---- Lab System calls

MIT6.S081 ---- Lab System calls

Lab System calls

物理資源抽象設計(檔案系統管理,記憶體使用,CPU切換,管道互動);指令執行的三種狀態(M,S,U),系統呼叫;巨集核心和微核心;程序機制(S/U mode,地址空間,時間片),程序目的(增強隔離性),程序的地址空間和狀態項,程序的設計思想;xv6的啟動概述;安全相關。
學習系統呼叫的原理和實現。

Lab準備

calling convention

RISC-V的calling約定一般是在暫存器間傳參,最多有8個整數暫存器(a0-a7)和8個浮點暫存器(fa0-fa7)。

當在整數暫存器中傳參時,引數儲存在對齊的even-odd暫存器對中,even暫存器儲存最低有效位。RV32中,如函式void foo(int, long long)

第一個引數傳給暫存器a0,第二個引數傳給暫存器a2和a3,沒有引數傳給a1。超過指標字大小兩倍的引數通過引用傳遞。

struct中沒有通過引數暫存器傳遞的部分在棧上傳遞,棧指標sp指向第一個沒有在引數暫存器中傳遞的引數。

返回值儲存在暫存器a0和a1中或者暫存器fa0和fa1中。只有當浮點值是單獨的浮點值或者只有一兩個浮點陣列成的struct結構才可以通過浮點暫存器返回。其他兩個指標字大小的返回值用a0和a1返回。

更大的返回值在記憶體中傳遞,由呼叫者分配記憶體空間,將第一個引數的指標傳給被呼叫者。

系統呼叫的過程

initcode.S將exec的引數放在暫存器a0,a1中,將系統呼叫號放在暫存器a7中,系統呼叫號可以索引syscalls

陣列(函式指標表)(kernel/syscall.c:108)。執行ecall(對執行環境發request,在X(U,S,M)-mode下執行ecall,將會產生environmen-call-from-X-mode異常)指令trap進核心,執行uservec(執行從使用者空間產生的traps),執行usertrap(處理來自使用者空間的中斷、異常、系統呼叫),執行syscall

syscall(kernel/syscall.c:133)從trapframe->a7取出系統呼叫號,索引syscalls。呼叫系統呼叫實現sys_exec,將返回值存在trapframe->a0,這也是使用者空間呼叫的exec()

的返回值。系統呼叫通常返回負數表示errors,0或者正數表示success。如果系統呼叫號無效,syscall輸出error並返回-1。

系統呼叫的引數

核心中的系統呼叫實現需要找到使用者程式碼傳遞的引數。因為使用者程式碼呼叫的是封裝好的函式,RISC-V calling convention約定將引數放在暫存器中。核心的trap程式碼將使用者暫存器的內容存在程序的trap frame中,供核心使用。核心函式argintargaddrargfd取出放在trap frame中的指定系統呼叫的引數,它們都是呼叫argraw(kernel/syscall.c)。

一些系統呼叫使用指標作為傳參,核心必須使用這些指標讀寫使用者記憶體。例如,exec系統呼叫傳給核心指向儲存在使用者空間的引數陣列的指標陣列。這些指標帶來兩個挑戰:

  • 使用者程式碼可能有bugs或是惡意程式碼,或者傳給核心非法的指標,或者指標有意指向核心空間而非使用者空間。
  • xv6核心頁表對映和使用者頁表對映不同,所以核心不能使用普通的指令從使用者提供的地址存取資料。

核心實現了對使用者提供的地址進行安全資料傳輸的函式。這裡下章具體學習,本章學會使用copyout即可完成實驗。

System call tracing

實現一個系統呼叫,能列印指定的系統呼叫的資訊。
通過接收引數mask,確定指定的系統呼叫,在系統呼叫將返回時,打印出它的資訊(程序id,系統呼叫名稱,返回值)。子程序繼承這個mask,不影響其他程序。

struct proc中加入int tracemask,用於確定需要trace哪些系統呼叫。這個屬性不需要加鎖。在allocprocfreeproc中對這個屬性進行初始化和清除。

syscall(kernel/syscall.c)中加入實現

if ((p->tracemask & (1 << num)) != 0) {
    printf("%d: syscall %s -> %d\n", p->pid, syscallnames[num-1], p->trapframe->a0);
}

在(kernel/sysproc.c)中實現sys_trace

// Set mask that specify which system calls to trace.
uint64
sys_trace(void)
{
    int tracemask;

    if(argint(0, &tracemask) < 0)
      return -1;

    myproc()->tracemask = tracemask;

    return 0;
}

其餘配置參照hints修改。

Sysinfo

實現一個系統呼叫:收集xv6的空閒記憶體大小和程序數量,並將值返回給使用者空間。

在(kernel/sysproc.c)中完成sysinfo系統呼叫的核心實現sys_sysinfo

// Get the number of bytes of free memory,
// and the number of processes whose state is not UNUSED.
uint64
sys_sysinfo(void)
{
    uint64 uinfop; // user pointer to struct sysinfo

    if(argaddr(0, &uinfop) < 0)
        return -1;

    return setsysinfo(uinfop);
}

在(kernel/proc.c)完成setsysinfoprocnum。分別實現將sysinfo資訊傳給使用者空間和統計程序數量。

// collect the nunmber of processes.
// Return the number of processes whose state is not UNUSED.
uint64
procnum(void)
{
    struct proc *p;
    uint64 nproc = 0;

    for (p = proc; p < &proc[NPROC]; p++) {
        acquire(&p->lock);
        if (p->state != UNUSED) {
            nproc++;
        }
        release(&p->lock);
    }

    return nproc;
}

// Set struct sysinfo and copy back to user space.
int
setsysinfo(uint64 addr)
{
    struct proc *p = myproc();
    struct sysinfo info;

    info.freemem = kremainsize();
    info.nproc = procnum();

    if(copyout(p->pagetable, addr, (char *)&info, sizeof(info)) < 0)
      return -1;
    return 0;
}

在(kernel/kalloc.c)完成kremainsize,實現空閒空閒記憶體的統計。

// collect the amount of free memory.
// Returns the number of bytes of free memory.
uint64
kremainsize(void)
{
    struct run *r;
    uint64 freepg = 0;

    acquire(&kmem.lock);
    r = kmem.freelist;
    while (r) {
        freepg++;
        r = r->next;
    }
    release(&kmem.lock);

    return freepg << 12;
}