xv6:labs2 syscall
lab2
1、lab2的內容總結:關于系統調用整個跟蹤過程:
使用系統調用時,用戶態會通過軟中斷(trap,陷阱)進入內核中,由trap識別中斷來自系統調用,然后調用syscall函數,
跟蹤過程:
1、打開gdb:
2、跟蹤用戶態trace執行過程:
首先執行以下兩條指令,為trace的main函數打上斷點
file user/_trace
b main
三次c之后成功進入trace的main函數
接下來單步運行到exec語句,這里有點問題,s跳入函數時只能跳入atoi,而trace沒有反應。所以trace(atoi(argv[1]))中的trace作用是什么?
s跳入exec函數,會進入usys.S文件:
si單步執行,然后打印出a7的值,其實就是exec的系統調用索引(該索引在syscall.h和syscall.c中規定),所以系統調用號被存儲在了a7中,后面進入內核態syscall.c會讀出a7的內容并調用系統調用函數:
再次執行si,ecall之后發現是進不去的,根據文檔描述,ecall之后會進入內核態,首先跳轉到trampoline.S文件的usevec函數:
.globl uservec
uservec:
#
# trap.c sets stvec to point here, so
# traps from user space start here,
# in supervisor mode, but with a
# user page table.
#
# sscratch points to where the process's p->trapframe is
# mapped into user space, at TRAPFRAME.
#
# swap a0 and sscratch
# so that a0 is TRAPFRAME
csrrw a0, sscratch, a0
# save the user registers in TRAPFRAME
sd ra, 40(a0)
sd sp, 48(a0)
sd gp, 56(a0)
sd tp, 64(a0)
sd t0, 72(a0)
sd t1, 80(a0)
sd t2, 88(a0)
sd s0, 96(a0)
sd s1, 104(a0)
sd a1, 120(a0)
sd a2, 128(a0)
sd a3, 136(a0)
sd a4, 144(a0)
sd a5, 152(a0)
sd a6, 160(a0)
sd a7, 168(a0)
sd s2, 176(a0)
sd s3, 184(a0)
sd s4, 192(a0)
sd s5, 200(a0)
sd s6, 208(a0)
sd s7, 216(a0)
sd s8, 224(a0)
sd s9, 232(a0)
sd s10, 240(a0)
sd s11, 248(a0)
sd t3, 256(a0)
sd t4, 264(a0)
sd t5, 272(a0)
sd t6, 280(a0)
# save the user a0 in p->trapframe->a0
csrr t0, sscratch
sd t0, 112(a0)
# restore kernel stack pointer from p->trapframe->kernel_sp
ld sp, 8(a0)
# make tp hold the current hartid, from p->trapframe->kernel_hartid
ld tp, 32(a0)
# load the address of usertrap(), p->trapframe->kernel_trap
ld t0, 16(a0)
# restore kernel page table from p->trapframe->kernel_satp
ld t1, 0(a0)
csrw satp, t1
sfence.vma zero, zero
# a0 is no longer valid, since the kernel page
# table does not specially map p->tf.
# jump to usertrap(), which does not return
jr t0
userevec函數保存了用戶寄存器,執行一些列操作(加載內核棧頂指針巴拉巴拉,不是很懂)。然后jump到usertrap()函數中,通過trap進入內核態。因為ecall之后不知道為什么進不去內核,所以接下來跟蹤內核態重開gdb。
3、跟蹤內核態sys_trace:
進入內核態后首先會進入usertrap函數,所以先在usertrap函數達斷點
b kernel/trap.c:usertrap
之后一直按c執行,內核加載完畢,輸入指令trace 32 echo,執行后會進入usertrap函數
單步n執行到syscall(),usertrap會進入系統調用,不過剛開始的系統調用是shell產生的系統調用,然后才是trace
可以在syscall的166行處打個斷點,每次產生系統調用的時候打印num(p num),查看系統調用編號,trace的編號為22 。
在syscall函數中其實可以發現,系統調用編號num存在 p->trapfram->a7,通過函數指針數組(syscalls)以及傳入的數組索引(num),操作系統會執行對應的系統調用,系統調用的返回值存入 p->trapfram->a0。
接下來可以在b sysproc.c:sys_trace ,在sys_trace處打一個斷點后按c進入,也可以直接單步執行到168行,按s步入sys_trace函數。
執行完syscall函數后,會回到usertrap函數中,執行usertrapret()函數,該函數的作用在于返回用戶態:
trace系統調用到此結束,接下來trace會根據傳入的參數32 echo執行exec(echo)系統調用。
2、trace
syscall.c
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](); // 調用syscalls[num], 返回值存入a0
// printf("num :%d , syscall mask :%d syscall&num :%d\n", num, p->syscall_mask, p->syscall_mask & num);
if (p->syscall_mask > 0 && (p->syscall_mask & (1 << num))) // trace here
{
printf("%d: syscall %s -> %d\n", p->pid, syscall_name[num], p->trapframe->a0);
}
}
else
{
printf("%d %s: unknown sys call %d\n", p->pid, p->name, num);
p->trapframe->a0 = -1;
}
}
sysproc.c
// sys_trace implement here.
uint64
sys_trace(void)
{
// acquire(&tickslock);
argint(0, &myproc()->syscall_mask); // for trace, get syscall_mask
// printf("%d\n",myproc()->syscall_mask);
// release(&tickslock);
return 0;
}
3、sysinfo
kalloc.c
uint64 FreeMem = 0; //labs 2 sysinfo
uint64
getFreeMem() //labs2 sysinfo
{
return FreeMem * 4096;
}
void kfree(void *pa)
{
struct run *r;
if (((uint64)pa % PGSIZE) != 0 || (char *)pa < end || (uint64)pa >= PHYSTOP)
panic("kfree");
// Fill with junk to catch dangling refs.
memset(pa, 1, PGSIZE);
r = (struct run *)pa;
acquire(&kmem.lock);
r->next = kmem.freelist;
kmem.freelist = r;
FreeMem++; // lab2 here
release(&kmem.lock);
}
void *
kalloc(void)
{
struct run *r;
acquire(&kmem.lock);
r = kmem.freelist;
if (r)
{
kmem.freelist = r->next;
FreeMem--; // lab2 here
}
release(&kmem.lock);
if (r)
memset((char *)r, 5, PGSIZE); // fill with junk
return (void *)r;
}
proc.c
uint64 procs = 0; // lab2 sysinfo
// labs 2 sysinfo
uint64
getProcs()
{
return procs;
}
//這個函數是本來就有的,但是需要對allocproc時,進行procs++;
static struct proc *
allocproc(void)
{
struct proc *p;
for (p = proc; p < &proc[NPROC]; p++)
{
acquire(&p->lock);
if (p->state == UNUSED)
{
goto found;
}
else
{
release(&p->lock);
}
}
return 0;
found:
p->pid = allocpid();
// Allocate a trapframe page.
if ((p->trapframe = (struct trapframe *)kalloc()) == 0)
{
release(&p->lock);
return 0;
}
procs++; // labs2 here
// printf("procs :%d\n", getProcs());
// An empty user page table.
p->pagetable = proc_pagetable(p);
if (p->pagetable == 0)
{
freeproc(p);
release(&p->lock);
return 0;
}
//freeproc時,對procs--;
static void
freeproc(struct proc *p)
{
if (p->trapframe)
kfree((void *)p->trapframe);
p->trapframe = 0;
if (p->pagetable)
proc_freepagetable(p->pagetable, p->sz);
p->pagetable = 0;
p->sz = 0;
p->pid = 0;
p->parent = 0;
p->name[0] = 0;
p->chan = 0;
p->killed = 0;
p->xstate = 0;
p->state = UNUSED;
procs--; // labs2 here
// printf("procs :%d\n", getProcs());
}
sysproc.c
extern uint64 getProcs();
extern uint64 getFreeMem();
//`sysinfo`需要將一個`struct sysinfo`復制回用戶空間
uint64
sys_sysinfo(void)
{
struct sysinfo
{
uint64 freemem;
uint64 procs;
} sysinfo;
sysinfo.freemem = getFreeMem();
sysinfo.procs = getProcs();
uint64 addr;
if (argaddr(0, &addr) < 0){ //獲取參數(一個虛地址,也就是用戶空間的sysinfo的地址)
return -1;
}
// copyout:將sysinfo指向的sizeof(sysinfo)大小的內存存到addr所指內存中
if(copyout(myproc()->pagetable, addr, (char *)&sysinfo, sizeof(sysinfo))<0){
return -1;
};
// printf("procs :%d\n", getProcs());
// printf("freemem :%d\n", getFreeMem());
return 0;
}
總結
以上是生活随笔為你收集整理的xv6:labs2 syscall的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Acwing4244牛的比赛
- 下一篇: 手撕Vue-Router-实现route