xv6实验课程--系统调用
本文來源:
https://mp.weixin.qq.com/s/kPwvXMZ2cv8uQNZIsKcAjA在上一個實驗中,你使用系統調用編寫了一些實用程序。在本實驗中,你將向xv6添加一些新的系統調用,這將幫助你了解它們是如何工作的,同時,讓你了解xv6內核的一些內部結構。在以后的實驗中你可能會添加更多的系統調用。?
在開始編碼之前,請閱讀xv6手冊的第2章、第4章的4.3節、4.4節以及下面所列相關源文件:
系統調用的用戶空間代碼:在user/user.h和user/usys.pl中。
內核空間代碼:在kernel/syscall.h和kernel/syscall.c中。
與進程相關的代碼:在kernel/proc.h和kernel/proc.c中。?
開始本實驗前,請切換到syscall分支:
? $ git fetch
? $ git checkout syscall
? $ make clean
1. 系統調用跟蹤?(難度:中等)?
任務:在xv6中添加一個系統調用跟蹤功能,該功能可幫助你在以后的實驗中調試程序。您將創建一個新的trace系統調用來控制跟蹤。它應該有一個參數,一個整數“mask”,其位指定要跟蹤的系統調用。例如,為了跟蹤fork系統調用,程序調用trace(1<<SYS_fork),其中SYS_fork是kernel/syscall.h中的syscall編號。如果在掩碼中設置了系統調用的編號,則必須修改xv6內核,以便在每個系統調用即將返回時輸出一行。該行應包含進程id、系統調用的名稱和返回值;不需要輸出系統調用參數。trace系統調用應啟用對調用它的進程及其隨后派生的任何子進程的跟蹤,但不應影響其他進程。
我們提供了一個跟蹤用戶級程序,該程序運行另一個啟用了跟蹤的程序(請參見user/trace.c)。完成后,您應該看到如下輸出:
例1: trace調用grep僅僅跟蹤read系統調用。32是1<<SYS_read。注:輸出的第一段是進程標識符PID,第二段是系統調用名稱,第三段是系統調用返回碼。
例2: trace跟蹤所有運行grep時調用的系統調用,其中2147583647的低31位都為1。
例3: 程序沒有被跟蹤,因此沒有打印跟蹤輸出。
例4:usertests中的forkforkfork測試的所有后代的fork系統調用都被跟蹤。
如果程序的行為如上所示,則你的實驗方案是正確的(盡管進程ID可能不同)。
提示:
●?將$U/_trace添加到Makefile的UPROGS中
●?運行make qemu,您將看到編譯器無法編譯user/trace.c,這是因為系統調用的用戶空間的存根(stubs)還不存在:將系統調用的原型添加到user/user.h,將存根添加到user/usys.pl中,并將syscall編號添加到kernel/syscall.h。Makefile調用perl腳本user/usys.pl,它生成user/usys.S,即實際的系統調用存根,它使用RISC-V ecall指令轉換到內核。一旦你修復了編譯問題,運行trace 32 grep hello README;它將失敗,因為您尚未在內核中實現系統調用。
●?在kernel/sysproc.c中添加一個sys_strace( )函數,通過在proc結構的新變量中記住其參數來實現新的系統調用(請參見kernel/proc.h)。從用戶空間檢索系統調用參數的函數在kernel/syscall.c中,您可以在kernel/sysproc.c中看到它們的使用示例。
●?修改fork( )(參見kernel/proc.c)將跟蹤掩碼從父進程復制到子進程。
●?修改kernel/syscall.c中的syscall( )函數以輸出跟蹤輸出。您需要添加一個syscall名稱數組來索引到其中。
實驗參考步驟
步驟1:用戶接口代碼的修改
(1) 在user/user.h中添加系統調用函數的定義。
(2) 在user/usys.pl中添加入口entry("trace")。
在make時會調用usys.pl生成user/usys.S匯編程序。在該匯編程序中,每個函數有五行,其中有三條指令,其功能是將系統調用號通過li(load?imm)存入a7寄存器,之后使用ecall指令進入內核態,最后返回。
make后user/usys.S中將出現下圖中.global trace開始的5行代碼。
步驟2:內核代碼的修改
(1)在kernel/syscall.h中定義系統調用號。
(2)在kernel/syscall.c的syscalls函數指針數組中添加對應的函數。
在syscall函數中,可讀取trapframe->a7獲取系統調用號,之后根據該系統調用號查找syscalls數組中的對應的處理函數并調用。
(3)在proc結構體中添加一個trace_mask字段,之后在創建子進程的fork函數中復制該字段到新進程。
kernel/proc.h
kernel/proc.c
(4)系統調用sys_trace的實現。在sysproc.c中添加函數uint64 sys_trace(void),該函數通過argint函數讀取參數賦值給mask變量,然后與trace_mask字段位或即可。
(5) 修改syscall函數,當系統調用號和trace_mask匹配時輸出相關信息。
注意上圖中的黃線處的syscall_name[num],這個需要定義。如下圖所示。
步驟3:編寫應用工具(注:源代碼中已提供)
user/trace.c
步驟4:修改Makefile,將$U/_trace添加到Makefile的UPROGS中。
步驟5:make qemu,然后測試。
2. Sysinfo (難度:中等)?
任務:增加一個sysinfo系統調用,它收集有關運行系統的信息。該系統調用有一個參數,即指向結構sysinfo的指針(參見kernel/sysinfo.h)。內核為該結構的各個字段賦值:設置freemem字段為可用內存的字節數,設置nproc字段為狀態是非UNUSED的進程數。實驗提供了一個測試程序sysinfotest,如果輸出“sysinfotest:OK”,則該任務通過。
提示:
●?將$U/_sysinfotest添加到Makefile的UPROGS中。
●?運行make qemu,user/sysinfotest.c將無法編譯。添加系統調用sysinfo,步驟與前面的trace系統調用相同。要在user/user.h中聲明sysinfo( )的原型,預先聲明struct sysinfo的存在:
struct sysinfo;
int sysinfo(struct sysinfo *);
●?修改上述編譯問題后,運行sysinfotest將會失敗,因為你尚未在內核中實現系統調用。
● sysinfo需要將struct sysinfo復制到用戶空間,請參閱sys_fstat( )(kernel/sysfile.c)和filestat( )(kernel/file.c)以獲取如何使用copyout()執行此操作的示例。
● 請在kernel/kalloc.c中添加一個函數,收集可用內存量。
●?請在kernel/proc.c中添加一個函數,收集進程數。
實驗參考步驟
步驟1:用戶接口代碼的修改
(1) 在user/user.h中聲明struct sysinfo的存在。(注:struct sysinfo在kernel/sysinfo.h中定義。
(2) 在user/user.h中添加系統調用函數的定義。
(3) 在user/usys.pl中添加入口 entry("sysinfo")。
在make時會調用usys.pl生成user/usys.S匯編程序。在該匯編程序中,每個函數有五行,其中有三條指令,其功能是將系統調用號通過li(load imm)存入a7寄存器,之后使用ecall指令進入內核態,最后返回。
make后user/usys.S中將出現下圖中.global sysinfo開始的5行代碼。
步驟2:內核代碼的修改
(1)在kernel/syscall.h中定義系統調用號。
(2)在kernel/syscall.c的syscalls函數指針數組中添加對應的函數及函數名。
(3)freemem()函數的實現。
閱讀kalloc和kfree兩個函數可知,kmem.freelist是一個保存了當前空閑內存塊的鏈表,因此只需要統計這個鏈表的長度再乘以PGSIZE就可以得到空閑內存。(注:kalloc和kfree兩個函數在kernel/kalloc.c文件中。)
在kernel/defs.h中聲明freemem函數。
在kernel/kalloc.c中添加freemem函數。
// get free memory uint64 freemem(void) {uint64 counter = 0;struct run *r;acquire(&kmem.lock); // 上鎖r = kmem.freelist; // 空閑塊鏈表while(r){ // 遍歷空閑塊鏈表,統計空閑塊塊數r = r->next;++counter;}release(&kmem.lock); // 釋放自旋鎖return counter * PGSIZE; // 返回空閑存儲空間大小,單位字節(B) }(4)nproc()函數的實現。
閱讀procdump和相關代碼可知,xv6的進程結構體保存在proc[NPROC]數組中。而proc->state字段保存了進程的當前狀態,有UNUSED、SLEEPING、RUNNABLE、RUNNING、ZOMBIE五種狀態。因此只需要遍歷這個數組,統計state不是UNUSED狀態的就行了。
在kernel/defs.h中聲明nproc函數。
在kernel/proc.c中添加nproc函數。
// get number of proc uint64 nproc(void) {uint64 counter = 0;struct proc *p;// 遍歷進程控制塊,即OS課程中的PCBfor(p = proc; p < &proc[NPROC]; p++) { acquire(&p->lock);if(p->state != UNUSED) {++counter;}release(&p->lock);}return counter; }(5)系統調用sys_sysinfo的實現。在sysproc.c中添加函數uint64 sys_sysinfo(void)。該函數主要通過freemem和nproc兩個函數來統計空閑內存量和進程數。首先在kernel/proc.c中添加頭文件#include "sysinfo.h"
添加sys_sysinfo(void)系統調用代碼。
?
// get sysinfo uint64 sys_sysinfo(void) {uint64 info; // user pointerstruct sysinfo kinfo;struct proc *p = myproc();if(argaddr(0, &info) < 0){return -1;}kinfo.freemem = freemem();kinfo.nproc = nproc();if(copyout(p->pagetable, info, (char*)&kinfo, sizeof(kinfo)) < 0){return -1;}return 0; }步驟3:編寫應用工具(注:源代碼中已提供了測試程序sysinfotest)
步驟4:修改Makefile,將$U/_sysinfotest添加到Makefile的UPROGS中。
步驟5:make qemu,然后測試。
? ? ? ? ?make?grade
參考資料
[1] https://pdos.csail.mit.edu/6.828/2021/labs/syscall.html
[2] https://www.cnblogs.com/YuanZiming/p/14218997.html
總結
以上是生活随笔為你收集整理的xv6实验课程--系统调用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: centos 安装trace_前期的准备
- 下一篇: React绑定事件处理函数this的几种