这个实验探索系统调用是如何通过trap实现的,会涉及到汇编代码和寄存器操作,建议先参考xv6手册以及xv6源码分析--trap机制。
系统调用号保存在a7,参数保存在a0--a6,一般syscall会返回一个uint64放入到a0中,至于其他参数会在内核态直接写入用户指定的位置。printf中的参数13会被放在a2中。
汇编代码main函数中没有找到调用f的地方,应该是被内联取代函数调用了,直接设置为12。
auipc rd, immediate意义是rd=immetiate<<12+pc。
void main(void) {
1c: 1141 addi sp,sp,-16
1e: e406 sd ra,8(sp)
20: e022 sd s0,0(sp)
22: 0800 addi s0,sp,16
printf("%d %d\n", f(8)+1, 13);
24: 4635 li a2,13
26: 45b1 li a1,12
28: 00000517 auipc a0,0x0
2c: 7b050513 addi a0,a0,1968 # 7d8 <malloc+0xea>
//将0x0左移12位+pc值=pc值,
30: 00000097 auipc ra,0x0
34: 600080e7 jalr 1536(ra) # 630 <printf>
exit(0);
38: 4501 li a0,0
3a: 00000097 auipc ra,0x0
3e: 27e080e7 jalr 638(ra) # 2b8 <exit>
进入main函数时各个寄存器状态如下:
然后执行完auipc和addi后,寄存器状态:
最终jalr跳转到0x3ae位置,这就是printf syscall用户态地址。
5221.
debugging时会有一个函数调用链,能够帮助我们发现在什么位置出现了什么error,实现这个功能,打印内核函数调用链。
void backtrace(void){
printf("backtrace: \n");
uint64 fp=r_fp();
uint64 top=PGROUNDUP(fp);
uint64 bottom=PGROUNDDOWN(fp);
while (fp<top && fp>bottom){
uint64 last_fp=*(uint64*)(fp-16);
uint64 ra=*(uint64*)(fp-8);
printf("%p\n",ra);
fp=last_fp;
}
}
定时通知进程使用cpu的时间,这对一些需要限制cpu使用的进程有帮助。
整个处理过程如图所示:
我们需要先在user 目录下添加系统调用声明代码,然后在kernel中添加系统调用实现,会从用户态传递两个参数。我们需要将这两个参数放入到proc结构体来维护,并且维护一个字段来追踪距离上次调用alarm间隔。
uint64
sys_sigalarm(void){
int ticks;
uint64 handleraddr;
if(argint(0, &ticks) < 0)
return -1;
if(argaddr(1, &handleraddr) < 0)
return -1;
struct proc *p=myproc();
if(ticks==0||handleraddr==0){
p->tick_interval=0;
p->alarm_handler=0;
}else{
p->alarm_handler=(void (*)())handleraddr;
}
return 0;
}
//proc.c
struct proc {
struct spinlock lock;
//...
//for Lab 4,定时alarm
int tick_interval;
void (*alarm_handler)(void);
int current_tick_num;
//...
};
每隔一个tick就会产生一个硬件时钟中断将进程yield出去,这个由usertrap处理,可以在这里判断是否需要执行进程的alarm_handler。alarm_handler是用户页表地址,内核页表没有对应页表项,所以需要切换到用户态去执行,结束后通过sigreturn进入到内核态。
void
usertrap(void)
{
int which_dev = 0;
//...
if(r_scause() == 8){
// system call
//...
} else if((which_dev = devintr()) != 0){
// ok
// printf("which_dev: %d, p->in_handler: %d\n",which_dev,p->in_handler);
if(which_dev==2&&p->in_handler==0){
p->current_tick_num++;
//时钟中断时决定是否执行alarm_handler,并且不能够重入
if(p->current_tick_num>=p->tick_interval && p->tick_interval!=0){
acquire(&p->lock);
p->in_handler=1;
p->current_tick_num=0;
p->alarmframe=*p->trapframe;
//返回用户态,handler位置,执行完sigreturn进内核
//此次回到用户态是为了执行handler
p->trapframe->epc=p->alarm_handler;
release(&p->lock);
}
}
} else {
printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid);
printf(" sepc=%p stval=%p\n", r_sepc(), r_stval());
p->killed = 1;
}
if(p->killed)
exit(-1);
// give up the CPU if this is a timer interrupt.
if(which_dev == 2){
if(!p->in_handler){
//printf("pid: %d inhandler\n",p->pid);
yield();
}
//如果in_handler=1,就直接返回用户态处理alarm_handler
}
usertrapret();
}
//sysproc.c
uint64
sys_sigalarm(void){
int ticks;
uint64 handleraddr;
if(argint(0, &ticks) < 0)
return -1;
if(argaddr(1, &handleraddr) < 0)
return -1;
struct proc *p=myproc();
p->tick_interval=ticks;
p->alarm_handler=handleraddr;
// printf("sigalarm ,interval: %d, handler: %ld\n",p->tick_interval,p->alarm_handler);
return 0;
}
uint64
sys_sigreturn(void){
// printf("sigreturn ,\n");
struct proc *p=myproc();
acquire(&p->lock);
*p->trapframe=p->alarmframe;
p->in_handler=0;
release(&p->lock);
return 0;
}
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。