xv6 lab4

RISC-V assembly

  1. Which registers contain arguments to functions? For example, which register holds 13 in main's call to printf?
    a0-a7 a2
  2. Where is the call to function f in the assembly code for main? Where is the call to g? (Hint: the compiler may inline functions.)
    所有都内连了,没有调用。相当于函数直接嵌入了。
  3. At what address is the function printf located?
    0000000000000640
 63a:	6442                	ld	s0,16(sp)
 63c:	6161                	addi	sp,sp,80
 63e:	8082                	ret

0000000000000640 <printf>:

void
printf(const char *fmt, ...)
{
 640:	711d                	addi	sp,sp,-96
 642:	ec06                	sd	ra,24(sp)
 644:	e822                	sd	s0,16(sp)
 646:	1000                	addi	s0,sp,32
  1. What value is in the register ra just after the jalr to printf in main?
    38

  2. Run the following code.

    unsigned int i = 0x00646c72;
    printf("H%x Wo%s", 57616, &i);

What is the output? Here's an ASCII table that maps bytes to characters.

The output depends on that fact that the RISC-V is little-endian. If the RISC-V were instead big-endian what would you set i to in order to yield the same output? Would you need to change 57616 to a different value?

Here's a description of little- and big-endian and a more whimsical description.
He110 World
需要,因为大端存储将高字节放在低地址,这样打印字符串就是64(d) 6c(l) 72(r)的顺序,所以需要反过来存,一次读取一个字节也就是8位 726c64。
第一个输出的只是16进制,所以换大小端读取的数据是相同的,不需要更换。
6. In the following code, what is going to be printed after 'y='? (note: the answer is not a specific value.) Why does this happen?

printf("x=%d y=%d", 3);

这里会读取a0,a1寄存器的值,只给了a0的值。输出a1的值就是之前的值

Backtrace

  • Xv6 allocates one page for each stack in the xv6 kernel at PAGE-aligned address. You can compute the top and bottom address of the stack page by using PGROUNDDOWN(fp) and PGROUNDUP(fp) (see kernel/riscv.h. These number are helpful for backtrace to terminate its loop.
    xv6会给每一个栈分配一页,所以就在这一页去找依次的调用。注意栈是从高往低增长,所以要回溯,最后的起点一定是页的第一个地址,所以使用PGROUNDUP得到起始地址。
    ![[Pasted image 20230201110540.png]]
  1. kernel/defs.h增加backtrace的声明
void backtrace(void));
  1. kernel/riscv.h增加寻找当前栈指针的函数
static inline uint64
r_fp()
{
  uint64 x;
  asm volatile("mv %0, s0" : "=r" (x) );
  return x;
}
  1. 实现backtrace函数
// 这里需要注意的是打印和更新的都是fp指向地址内的值,所以需要解引用
void
backtrace(void)
{
  uint64 fp = r_fp();
  while (fp != PGROUNDUP(fp))
  {
    printf("%p\n", *(uint64*)(fp - 8));
    fp = *(uint64*)(fp - 16);
  }
  return;
}

Alarm

增加一个在进程使用cpu时定期警报的功能。

  • 增加sigalarm(n, fn)系统调用 系统时钟每n次后,调用fn。在user/alarmtest.c中有user调用。
  • alarmtest 调用 sigalarm(2, periodic)
  • alarmtest usertests 进行验证
  1. 给sigalarm和sigreturn添加系统调用(这里第二个lab有,参照加上就可以)
  2. struct proc增加参数,从而支撑下面的行为
struct proc{
...
//增加
int alarm_interval;

uint64 handler_va;

struct trapframe saved_trapframe;

int passed_ticks;

int have_return;
}
  1. 实现sigalarm
    sigalarm功能:
  • 拿到interval
  • 拿到handler的地址
  • 增加have_return从而标志当前是否已经有alarm了,防止时间太短,handler没有处理完,然后tick到了又进handler了。
uint64 
sys_sigalarm(void)
{
  int alarm_interval;
  uint64 handler_va;
  argint(0, &alarm_interval);
  argaddr(1, &handler_va);

  struct proc *p = myproc();
  p->alarm_interval = alarm_interval;
  p->handler_va = handler_va;
  p->have_return = 1;
  return p->trapframe->a0;
}
  1. 实现sigreturn (handler调用,从而返回调用的函数)
  • 恢复trapframe,因为切换到handler函数后,再回到内核可能会更改trapframe的值
  • 恢复have_return 从而可以让下一次进行
  1. 更改trap.c中usertrap()
    使得每次时钟周期都会进行相应的操作
 if(which_dev == 2)
//增加
  {
    if (p->alarm_interval && p->have_return)
    {
      if (++p->passed_ticks == p->alarm_interval)
      {
        p->saved_trapframe = *p->trapframe;
        p->trapframe->epc = p->handler_va;
        p->passed_ticks = 0;
        p->have_return = 0;
      }
    }
//增加
    yield();
  }
posted @   yych0745  阅读(79)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示