Lab Util report
Boot Xv6 (easy)
没啥复杂的,按照教程来就可以了,基本没踩坑。
Sleep
描述:Implement the UNIX program sleep for xv6; your sleep should pause for a user-specified number of ticks. A tick is a notion of time defined by the xv6 kernel, namely the time between two interrupts from the timer chip. Your solution should be in the file user/sleep.c.
指导(可以称得上是保姆级了。。。):
- Before you start coding, read Chapter 1 of the xv6 book.
*Look at some of the other programs in user/ (e.g., user/echo.c, user/grep.c, and user/rm.c) to see how you can obtain the command-line arguments passed to a program. - If the user forgets to pass an argument, sleep should print an error message.
- The command-line argument is passed as a string; you can convert it to an integer using atoi (see user/ulib.c).
- Use the system call sleep.
- See kernel/sysproc.c for the xv6 kernel code that implements the sleep system call (look for sys_sleep), user/user.h for the C definition of sleep callable from a user program, and user/usys.S for the assembler code that jumps from user code into the kernel for sleep.
- Make sure main calls exit() in order to exit your program.
- Add your sleep program to UPROGS in Makefile; once you’ve done that, make qemu will compile your program and you’ll be able to run it from the xv6 shell.
- Look at Kernighan and Ritchie’s book The C programming language (second edition) (K&R) to learn about C.
按照教程即可。代码如下:
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc, char *argv[])
{
int t;
if (argc <= 1 || argc > 2) {
fprintf(2, "usage: sleep <seconds>\n");
exit(1);
}
t = atoi(argv[1]);
sleep(t);
exit(0);
}
实现sleep
命令没啥好讲的,实际上好玩的是sleep syscall的实现,即如何从用户态陷入内核空间,进而完成sleep
命令的。
首先在user.h
中声明了sleep
这个函数,在usys.S
中看到,首先.global sleep
在全局符号表中注册函数名sleep
,之后调用sleep
这个系统调用时就会执行以下这段riscv汇编代码:
.global sleep
sleep:
li a7, SYS_sleep
ecall
ret
这段汇编的意思也很明确,就是把SYS_sleep
的值放入a7
寄存器中,记录系统调用的类型,之后的ecall
指令产生一个异常,此时陷入内核态,然后查看kernel/syscall.c
可知SYS_sleep
和sys_sleep
存在映射关系,syscall
函数可以根据SYS_<name>
值的不同执行不同的sys_<name>
函数(kernel/syscall.c:140)。
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]();
} else {
printf("%d %s: unknown sys call %d\n",
p->pid, p->name, num);
p->trapframe->a0 = -1;
}
}
反正目前看kernel/syscall.c
里的syscall
实现,是通过中断实现的(trapframe
),具体的实现会等到后面的实验再详细解释。
以上是一个基本的理解,更详细的了解估计在后面的课程中会涉及。
参考:
- riscv特权等级介绍:riscv特权等级
- riscv指令手册:riscv-spec
PingPong
描述:Write a program that uses UNIX system calls to ‘‘ping-pong’’ a byte between two processes over a pair of pipes, one for each direction. The parent should send a byte to the child; the child should print “: received ping”, where is its process ID, write the byte on the pipe to the parent, and exit; the parent should read the byte from the child, print “: received pong”, and exit. Your solution should be in the file user/pingpong.c.
指导:
- Use pipe to create a pipe.
- Use fork to create a child.
- Use read to read from the pipe, and write to write to the pipe.
- Use getpid to find the process ID of the calling process.
- Add the program to UPROGS in Makefile.
- User programs on xv6 have a limited set of library functions available to them. You can see the list in user/user.h; the source (other than for system calls) is in user/ulib.c, user/printf.c, and user/umalloc.c.
之前实现过unix的版本,直接把代码copy稍作改动即可。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main()
{
int p[2], q[2];
pipe(p), pipe(q);
if (fork() > 0) {
close(p[0]);
close(q[1]);
char x[1];
write(p[1], "0", 1);
read