[mit6.1810]Lab4: traps

【写在前面】

这是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();
  }

运行结果

测试程序结果:

2484996991@qq.com
使用 Hugo 构建
主题 StackJimmy 设计