【写在前面】
这是2023-2024春季学期操作系统课程有关xv6 lab部分的实验报告,参考了很多网络资源,解释也不一定正确,仅作为留档,参考需谨慎。
Lab 地址:6.1810 / Fall 2024
参考:
MIT 6.S081_Zheyuan Zou的博客-CSDN博客
yali-hzy/xv6-labs-2024: MIT 6.1810 assignments
【露说xv6】Lab4-traps_哔哩哔哩_bilibili
Alarm
实验任务分析
实验要求在xv6中新增一个特性:在进程使用CPU的时间内,xv6定期向进程发出警报。
test0: invoke handler
问题:需要添加一个新的系统调用sigalarm(interval, handler)。这个系统调用可以设置一个进程被时钟打断的间隔(Inteval)和响应函数(handler)。例如当调用了sigalarm(n, fn),这个进程就会以n的时间间隔调用处理函数fn。当fn返回时,应用应当在它离开的地方恢复执行。
实现思路:
-
sigalarm在内核的实现sys_sigalarm需要将用户传进来的参数记录到proc结构体中,此外还添加了一个用于表示当前进程是否处于响应alarm中的标志位
-
根据提示“Every tick, the hardware clock forces an interrupt, which is handled in usertrap() in kernel/trap.c. ”需要在usertrap()中找到对应时钟中断的位置进行处理:如果时间达到了我们设定的间隔,则触发alarm响应过程
1
2
|
// kernel/trap.c/usertrap()
if(which_dev == 2) ...
|
test1/test2()/test3(): resume interrupted code
问题:由于将中断处理函数放入到了进程的proc结构体中,当函数执行完毕后,我们需要对进程的现场进行恢复,所以需要添加另一个新的系统调用sigreturn。
实现思路:
-
在usertrap() 中将trapframe完全保存,设置trapframe中的epc寄存器的值设置为handler的虚拟地址,使其可以经由usertrapret返回到用户态下的handler函数中去
-
在sigreturn在内核的实现sys_sigreturn中:将trapframe恢复,并重置标志位
-
根据提示“Make sure to restore a0. sigreturn is a system call, and its return value is stored in a0. ”,返回a0
具体实现代码
准备工作:新增一个系统调用(类似Lab2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
// Makefile
UPROGS=\
$U/_cat\
......
$U/_alarmtest\
// user/user.h
int sigalarm(int, void (*handler)());
int sigreturn(void);
// user/usys.pl
entry("sigalarm");
entry("sigreturn");
// kernel/syscall.h
#define SYS_sigalarm 22
#define SYS_sigreturn 23
// kernel/syscall.c
extern uint64 sys_sigalarm(void);
extern uint64 sys_sigreturn(void);
[SYS_sigalarm] sys_sigalarm,
[SYS_sigreturn] sys_sigreturn,
|
在proc.h中声明新添加到进程结构体中的变量
1
2
3
4
5
|
struct trapframe *pre_trapframe; // 保存处理前的trapframe
int alarm_internel; // 触发alarm handler的时间间隔
uint64 handler; // handler在用户态的虚拟地址
int crt_ticks; // 记录从上次触发handler到现在经过的ticks数量
int Handling; // 标志位,表示当前进程是否处于响应alarm中
|
并在proc.c中初始化
1
2
3
4
5
6
7
8
9
|
//static struct proc* allocproc()
if((p->pre_trapframe = (struct trapframe *)kalloc()) == 0){
freeproc(p);
release(&p->lock);
return 0;
}
p->crt_ticks = 0;
p->handler = -1;
p->alarm_internel = 0;
|
sysproc.c添加两个新系统调用实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
uint64
sys_sigalarm(void)
{
int ticks;
uint64 handler;
// 从用户空间获取参数ticks和handler
argint(0,&ticks);
argaddr(1,&handler);
struct proc* p = myproc(); // 获取当前进程
p->crt_ticks = 0; // 重置计数器
p->alarm_internel = ticks; // 设施报警间隔
if(ticks ==0 && handler ==0){
p->handler = -1; // 如果ticks和handler都为 0,清除报警设置防止程序崩溃
}
else{
p->handler = handler; // 设置handler
}
return 0;
}
uint64
sys_sigreturn(void)
{
struct proc* p = myproc(); // 获取当前进程
memmove(p->trapframe, p->pre_trapframe, sizeof(struct trapframe)); // 恢复现场
p->crt_ticks = 0; // 重置计数器
p->Handling = 0; // 重置标志位
return p->trapframe->a0;
}
|
trap.c中修改usertrap()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// give up the CPU if this is a timer interrupt.
if(which_dev == 2){
if(!(p->handler == 0 && p->handler == -1)){ // 如果有成功获取参数
p->crt_ticks++; // 计数器计数
if(p->crt_ticks == p->alarm_internel && p->alarm_internel > 0){ // 检查是否达到alarm间隔
p->crt_ticks = 0; // 重置 tick 计数器
if(!p->Handling){ // 如果没有在处理alarm
p->Handling = 1; // 设置标志位
memmove(p->pre_trapframe, p->trapframe, sizeof(struct trapframe)); // 保存现场
p->trapframe->epc = p->handler; // 设置trapframe的epc为handler地址
}
}
}
yield();
}
|
运行结果


…

测试程序结果:
