Lab2: system calls
預(yù)備知識(shí)
執(zhí)行一次系統(tǒng)調(diào)用的流程:
USER MODE
step1:系統(tǒng)調(diào)用聲明
-
user/user.h
:系統(tǒng)調(diào)用函數(shù)(如int fork(void)
)
step2:ecall 進(jìn)入內(nèi)核態(tài)
-
user/usys.S
(該文件由user/usys.pl
生成,后續(xù)添加函數(shù)可以在這里添加):執(zhí)行如下命令
.global fork
fork:
li a7, SYS_fork
ecall
ret
- 將系統(tǒng)調(diào)用的編號(hào)(在
kernel/syscall.h
中定義)寫(xiě)入a7
寄存器 - 從
ecall
進(jìn)入中斷處理函數(shù)
KERNEL MODE
step3:保存數(shù)據(jù)并跳轉(zhuǎn)到中斷判斷函數(shù)
-
kernel/trampoline.S
:在uservec
中保存寄存器、切換到內(nèi)核棧、切換棧指針SP等,最后跳轉(zhuǎn)到內(nèi)核指定的中斷判斷函數(shù)usertrap
(在kernel/trap.c
中)- 每一個(gè)進(jìn)程都對(duì)應(yīng)一個(gè)狀態(tài)結(jié)構(gòu)體
proc
(在kernel/proc.h
中定義這個(gè)結(jié)構(gòu)體),將數(shù)據(jù)存儲(chǔ)在這里
- 每一個(gè)進(jìn)程都對(duì)應(yīng)一個(gè)狀態(tài)結(jié)構(gòu)體
step4:中斷判斷函數(shù)
-
kernel/trap.c
:中斷判斷函數(shù)usertrap
用于處理來(lái)自用戶態(tài)的中斷、異?;蛳到y(tǒng)調(diào)用,在此處判斷是否是系統(tǒng)調(diào)用,如果是,則執(zhí)行響應(yīng)函數(shù)syscall
step5:執(zhí)行對(duì)應(yīng)的系統(tǒng)調(diào)用
-
kernel/syscall.c
:響應(yīng)函數(shù)syscall
用于對(duì)號(hào)入座,根據(jù)給出的syscalls
表(將系統(tǒng)調(diào)用編號(hào)與執(zhí)行函數(shù)相對(duì)應(yīng))獲取系統(tǒng)調(diào)用類(lèi)型(系統(tǒng)調(diào)用編號(hào)從a7
寄存器中讀取),執(zhí)行相應(yīng)的函數(shù)(進(jìn)入kernel/sysproc.c
中);系統(tǒng)調(diào)用的返回值傳遞給了a0
,后續(xù)會(huì)回傳給用戶態(tài)
step6:系統(tǒng)調(diào)用函數(shù)
-
kernel/sysproc.c
:保存著系統(tǒng)調(diào)用的執(zhí)行函數(shù),如果系統(tǒng)調(diào)用有參數(shù),需要用argraw
(讀取參數(shù)本質(zhì)上是從內(nèi)存中恢復(fù),見(jiàn)kernel/syscall.c
)取出保存的寄存器數(shù)據(jù),然后函數(shù)最終都將調(diào)用相對(duì)應(yīng)的執(zhí)行函數(shù)(在kernel/proc.c
中)
step7:系統(tǒng)調(diào)用核心功能
-
kernel/proc.c
:這里的函數(shù)用于執(zhí)行核心功能,如創(chuàng)建進(jìn)程等
添加系統(tǒng)調(diào)用需要考慮的地方
-
在
user/user.h
中添加系統(tǒng)調(diào)用的函數(shù)聲明,并在user
中創(chuàng)建相應(yīng)的系統(tǒng)調(diào)用(*.c
文件)(和lab1類(lèi)似) -
在
user/usys.pl
中添加系統(tǒng)調(diào)用項(xiàng) -
在
kernel/syscall.h
添加系統(tǒng)調(diào)用的編號(hào) -
在
kernel/syscall.c
中的syscalls
表中添加映射關(guān)系,指出需要執(zhí)行的函數(shù) -
在
kernel/sysproc.c
和kernel/proc.c
中實(shí)現(xiàn)系統(tǒng)調(diào)用的執(zhí)行函數(shù)
Part1:System call tracing
實(shí)現(xiàn)功能
-
添加一個(gè)系統(tǒng)調(diào)用的
trace
功能,在命令前輸入trace <mask>
,能夠打印出該命令使用的系統(tǒng)調(diào)用 -
mask = 1 << SYS_name
為一個(gè)整數(shù),它能夠指定跟蹤哪個(gè)系統(tǒng)調(diào)用,SYS_name
是來(lái)自kernel/syscall.h
的一個(gè)系統(tǒng)調(diào)用號(hào),mask
可以等于1 << SYS_name | 1 << SYS_other_name
-
如果在輸入命令時(shí)使用
trace <mask>
,則在使用指定系統(tǒng)調(diào)用后應(yīng)該返回一行包括進(jìn)程id、系統(tǒng)調(diào)用名稱和返回值
的打印信息 -
trace
要求能夠跟蹤進(jìn)程以及進(jìn)程派生出的子進(jìn)程,且不影響其他進(jìn)程
實(shí)驗(yàn)提示
-
在
user/user.h
、user/usys.pl
、kernel/syscall.h
添加系統(tǒng)調(diào)用以及編號(hào) -
在
kernel/sysproc.c
添加一個(gè)sys_trace
函數(shù)實(shí)現(xiàn)新的系統(tǒng)調(diào)用,并在狀態(tài)結(jié)構(gòu)體proc
中插入一個(gè)新的變量(對(duì)該進(jìn)程進(jìn)行跟蹤的mask
掩碼),系統(tǒng)調(diào)用的執(zhí)行函數(shù)參考kernel/sysproc.c
-
此外需要對(duì)
kernel/proc.c
的fork
函數(shù)進(jìn)行修改,因?yàn)檎{(diào)用了trace
系統(tǒng)調(diào)用,會(huì)在當(dāng)前進(jìn)程狀態(tài)proc
(在kernel/proc.h
中)中修改mask
掩碼的設(shè)置,同時(shí)每次fork
時(shí)也需要對(duì)相關(guān)的子進(jìn)程同步相關(guān)的設(shè)置 -
需要修改
kernel/syscall.c
下的syscall
使其打印追蹤信息;為了打印系統(tǒng)調(diào)用名稱,需要額外創(chuàng)建字符串?dāng)?shù)組
實(shí)驗(yàn)代碼
-
在
user/user.h
中添加int trace(int);
的聲明(trace接受一個(gè)整型的參數(shù)mask
) -
在
user/usys.pl
中添加entry("trace");
,這是進(jìn)入內(nèi)核態(tài)的入口 -
在
Makefile
中的UPROGS
添加_trace
-
進(jìn)入內(nèi)核態(tài),在
kernel/syscall.h
添加系統(tǒng)調(diào)用的編號(hào)#define SYS_trace 22
-
在
kernel/syscall.c
中添加系統(tǒng)調(diào)用的映射extern uint64 sys_trace(void);
、[SYS_trace] sys_trace
-
在
kernel/proc.h
中的proc
中添加int mask
-
在
kernel/sysproc.c
中添加進(jìn)入系統(tǒng)調(diào)用的函數(shù)
uint64
sys_trace(void){ // 參考sys_wait函數(shù)
uint64 p;
if(argaddr(0, &p) < 0) // 獲取trace的參數(shù)(只有一個(gè))
return -1;
return trace(p); // 系統(tǒng)調(diào)用的執(zhí)行函數(shù)
}
- 在
kernel/proc.c
實(shí)現(xiàn)trace
的系統(tǒng)調(diào)用執(zhí)行函數(shù)(僅僅是進(jìn)行一個(gè)掩碼的賦值)
int
trace(int mask){
struct proc *p = myproc();
p->mask = mask; // 將掩碼賦值給結(jié)構(gòu)體的mask
return 0;
}
- 在
kernel/defs.h
(這個(gè)里面包含了kernel
中常用函數(shù)的原型聲明)中加入函數(shù)的聲明int trace(int)
- 在
kernel/proc.c
中的fork
函數(shù)中加一行代碼np->mask = p->mask;
,將父進(jìn)程的 mask 賦值給子進(jìn)程的 mask - 在
kernel/syscall.c
中對(duì)syscall
進(jìn)行修改,打印追蹤信息;并且為了打印系統(tǒng)調(diào)用的名稱,創(chuàng)建一個(gè)字符串?dāng)?shù)組
char
*sys_name[] = {
"", "fork", "exit", "wait", "pipe", "read", "kill", "exec",
"fstat", "chdir", "dup", "getpid", "sbrk", "sleep", "uptime", "open",
"write", "mknod", "unlink", "link", "mkdir", "close", "trace"
};
void
syscall(void)
{
int num;
struct proc *p = myproc();
num = p->trapframe->a7;
if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
p->trapframe->a0 = syscalls[num](); // 返回值
if(p->mask >> num & 1){ // 判斷是否有mask輸入
// 打印進(jìn)程id、系統(tǒng)調(diào)用的名稱和返回值
printf("%d: syscall %s -> %d", p->pid, sys_name[num], p->trapframe->a0); // 這里注意使用的prinrf是因?yàn)閤v6的kernel中專(zhuān)門(mén)定義了一個(gè)printf的函數(shù),在Linux內(nèi)核中只能使用prinrk打印
}
} else {
printf("%d %s: unknown sys call %d\n",
p->pid, p->name, num);
p->trapframe->a0 = -1;
}
}
p->mask >> num & 1
:p->mask 為 1<<SYS_read,num 為從 a7 寄存器中讀出來(lái)的 SYS_read,p->mask >> num 能夠?qū)?mask 還原為 SYS_read,如果沒(méi)有 mask,則不打印文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-856345.html
實(shí)驗(yàn)感悟
- 在閱讀源碼的過(guò)程中,看到有些地方涉及到匯編語(yǔ)言,之后要把這塊給學(xué)習(xí)一下,要不然很影響閱讀體驗(yàn)
- 雖然按照系統(tǒng)調(diào)用的步驟能把整個(gè)流程走下來(lái),但是每個(gè)函數(shù)以及它們之間的關(guān)系實(shí)際上還沒(méi)有特別清楚,需要再進(jìn)行梳理(將每個(gè)函數(shù)的作用都搞清楚)
- 在進(jìn)行
proc
修改的時(shí)候,要考慮到fork
函數(shù)的子進(jìn)程是否需要復(fù)制參數(shù)
Part2: Sysinfo
實(shí)現(xiàn)功能
- 添加一個(gè)系統(tǒng)調(diào)用
sysinfo
,通過(guò)系統(tǒng)調(diào)用將收集正在運(yùn)行的程序的信息 - 在這個(gè)系統(tǒng)調(diào)用中將傳入一個(gè)結(jié)構(gòu)體指針
sysinfo
,結(jié)構(gòu)體定義在kernel/sysinfo.h
中,其中freemem
應(yīng)該設(shè)置為空閑內(nèi)存的字節(jié)數(shù),nproc
應(yīng)該設(shè)置為進(jìn)程狀態(tài)不為UNUSED
的進(jìn)程數(shù)
實(shí)驗(yàn)提示
- 在
user/user.h
中聲明sysinfo()
的原型,你需要預(yù)先聲明結(jié)構(gòu)體sysinfo
的存在:struct sysinfo;
、int sysinfo(struct sysinfo *);
- sysinfo 需要將
struct sysinfo
復(fù)制回用戶空間;參考sys_fstat() (kernel/sysfile.c)
和filestat() (kernel/file.c)
學(xué)習(xí)使用copyout()
- 要收集空閑內(nèi)存量,可以在
kernel/kalloc.c
中添加一個(gè)函數(shù) - 要收集進(jìn)程數(shù),請(qǐng)?jiān)?
kernel/proc.c
中添加一個(gè)函數(shù)
實(shí)驗(yàn)代碼
- 在
user/user.h
添加sysinfo
結(jié)構(gòu)體和函數(shù)的聲明struct sysinfo;
、int sysinfo(struct sysinfo *);
- 分別在
user/usys.pl
、Makefile
、kernel/syscall.h
、kernel/syscall.c
執(zhí)行與上一個(gè)實(shí)驗(yàn)一樣的步驟 - 在
kernel/kalloc.c
中實(shí)現(xiàn)空閑內(nèi)存大小的查找
uint64
getfreemen(void){ // 獲取空閑內(nèi)存數(shù)量
struct run *rp;
uint64 result = 0;
acquire(&kmem.lock); // 考慮到并發(fā)問(wèn)題,上鎖
rp = kmem.freelist;
while(rp){
result += 1;
rp = rp->next;
}
release(&kmem.lock);
return result * PGSIZE; //一個(gè)內(nèi)存頁(yè)的大小為PGSIZE(4096)
}
- 在
kernel/proc.c
中實(shí)現(xiàn)進(jìn)程數(shù)的統(tǒng)計(jì)
int
getproc(void){
struct proc *p;
int result = 0;
for(p = proc; p < &proc[NPROC]; p++){
acquire(&p->lock); //上鎖
if(p->state != UNUSED){
result += 1;
}
release(&p->lock);
}
return result;
}
上述兩個(gè)步驟都需要在
kernel/defs.h
中添加函數(shù)聲明文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-856345.html
- 在
kernel/sysproc.c
中實(shí)現(xiàn)執(zhí)行函數(shù)sys_sysinfo
的功能,記得添加#include "sysinfo.h"
來(lái)使用sysinfo
結(jié)構(gòu)體
uint64
sys_sysinfo(void){
struct sysinfo info;
struct proc *p;
uint64 addr;
if(argaddr(0, &addr) < 0) // 獲取系統(tǒng)的指針參數(shù)
return -1;
p = myproc();
info.freemem = getfreemen();
info.nproc = getproc();
// 從內(nèi)核copy到用戶
if(copyout(p->pagetable, addr, (char *)&info, sizeof(info)) < 0) //參考sys_fstat(在kernel/sysfile.c中)
return -1;
return 0;
}
實(shí)驗(yàn)感悟
- 這個(gè)實(shí)驗(yàn)實(shí)現(xiàn)的內(nèi)存大小和進(jìn)程數(shù)的統(tǒng)計(jì)函數(shù)需要了解了 kalloc.c 和 proc.c 的函數(shù)后才能寫(xiě)出來(lái),我兩個(gè)文件看的還不太完全,之后需要再看看
- 這個(gè)實(shí)驗(yàn)中也使用了指針,這一塊使用還不太熟練,C 語(yǔ)言還需要精進(jìn)
到了這里,關(guān)于MIT6.S081 - Lab2: system calls的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!