【XV6】 system calls

代码:https://github.com/JasenChao/xv6-labs.git

使用GDB调试

安装risc-v的GDB

先安装依赖:

sudo apt-get install libncurses5-dev python2 python2-dev texinfo libreadline-dev

再下载源码,可以从清华镜像源下载:

wget https://mirrors.tuna.tsinghua.edu.cn/gnu/gdb/gdb-13.2.tar.xz

解压缩并编译安装:

tar xvf gdb-13.2.tar.xz
cd gdb-13.2/
mkdir build
cd build
../configure --prefix=/usr/local --target=riscv64-unknown-elf --enable-tui=yes
make -j$(nproc)
sudo make install

使用GDB

先用qemu运行xv6,在xv6目录下执行:

make qemu-gdb

此时应该打印一个端口号,例如tcp::26000,记住这个端口号,另起一个终端,运行gdb:

riscv64-unknown-elf-gdb

在GDB中连接这个端口,由于本次实验想在每次syscall中断时断点,所以file kernel/kernel

target remote localhost:26000
file kernel/kernel
b syscall

c让程序从断点处继续执行,输出如下:

(gdb) b syscall
Breakpoint 1 at 0x80002142: file kernel/syscall.c, line 243.
(gdb) c
Continuing.
[Switching to Thread 1.2]

Thread 2 hit Breakpoint 1, syscall () at kernel/syscall.c:243
243     {
(gdb) layout src
(gdb) backtrace

layout 命令将窗口一分为二,显示src,也可以layout asm等。

GDB有一些常用指令:

1.break/b (*地址)/函数名 #设置断点,指令地址可以查看asm文件
#除此之外通常可以使用b *$stvec,在trampoline处设置断点

2.continue/c #运行到断点处然后停下

3.stepi/si #运行单个汇编指令,进入函数

4.n #运行一行c语言代码,不进入函数

5.step/s #运行一行C语言代码,进入函数

6.info/i reg/r #查看32个同一寄存器和PC寄存器的值

7.info/i break/b #查看断点信息

8.info/i frame #打印当前栈帧

9.print/p (/x) 变量/$寄存器 #打印C语言变量值或寄存器的值,/x是将值为打印16进制的值

10.examining/x 地址 #检测内存单元并打印
                #可以使用/x十六进制 /i指令 /c字符格式化打印值
                #而且可以使用/4c打印4个字节单元并输出为字符
11.list 地址  #打印函数的源代码在指定的位置。

12.layout asm/src/split #分别打开汇编代码窗口、源码窗口、split打开汇编和源码窗口
                        #ctrl x + a 可以关闭layout窗口

添加系统调用-trace

题目要求基于已经给出的trace.c实现系统调用跟踪。根据提示按步骤进行。

  • $U/_trace\添加到MakefileUPROGS
  • user/user.h中添加系统调用int trace(int)
  • user/usys.pl中添加stub entry("trace")
  • kernel/syscall.h中添加系统调用编号#define SYS_trace 22
  • kernel/sysproc.c中添加函数sys_trace(),代码如下:
    uint64
    sys_trace(void)
    {
        int n;
        argint(0, &n);
        myproc()->trace_mask = n;
        return 0;
    }
    
  • kernel/syscall.c中添加命令名称数组,便于显示系统调用名,修改syscall函数,代码如下:
    static char syscall_name[23][16] = {"fork", "exit", "wait", "pipe", "read", "kill", "exec", "fstat", "chdir", "dup", "getpid", "sbrk", "sleep", "uptime", "open", "write", "mknod", "unlink", "link", "mkdir", "close", "trace", "sysinfo"};
    
    void
    syscall(void)
    {
    int num;
    struct proc *p = myproc();
    
    num = p->trapframe->a7;
    if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
        // Use num to lookup the system call function for num, call it,
        // and store its return value in p->trapframe->a0
        p->trapframe->a0 = syscalls[num]();
        if(p->trace_mask > 0 && (p->trace_mask & (1 << num)))
        {
        printf("%d: syscall %s -> %d\n", p->pid, syscall_name[num - 1], p->trapframe->a0);
        }
    } else {
        printf("%d %s: unknown sys call %d\n",
                p->pid, p->name, num);
        p->trapframe->a0 = -1;
    }
    }
    
  • kernel/syscall.c中还需要增加系统调用的函数指针:
    extern uint64 sys_trace(void);
    
    static uint64 (*syscalls[])(void) = {
    [SYS_fork]    sys_fork,
    [SYS_exit]    sys_exit,
    [SYS_wait]    sys_wait,
    [SYS_pipe]    sys_pipe,
    [SYS_read]    sys_read,
    [SYS_kill]    sys_kill,
    [SYS_exec]    sys_exec,
    [SYS_fstat]   sys_fstat,
    [SYS_chdir]   sys_chdir,
    [SYS_dup]     sys_dup,
    [SYS_getpid]  sys_getpid,
    [SYS_sbrk]    sys_sbrk,
    [SYS_sleep]   sys_sleep,
    [SYS_uptime]  sys_uptime,
    [SYS_open]    sys_open,
    [SYS_write]   sys_write,
    [SYS_mknod]   sys_mknod,
    [SYS_unlink]  sys_unlink,
    [SYS_link]    sys_link,
    [SYS_mkdir]   sys_mkdir,
    [SYS_close]   sys_close,
    [SYS_trace]   sys_trace,
    };
    
  • 修改kernel/proc.h中的struct proc结构体,增加int trace_mask
  • 修改kernel/proc.c中的fork函数,使得子进程可以继承父进程的trace_mask,增加的代码如下:
    acquire(&np->lock);
    np->trace_mask = p->trace_mask;
    release(&np->lock);
    

Makefile调用perl脚本user/usys.pl,该脚本生成user/usys.S,这是实际的系统调用,它使用RISC-V ecall指令转换到内核。

使用make GRADEFLAGS=trace grade测试代码是否通过。

添加系统调用-sysinfo

题目要求基于已经给出的sysinfotest.c实现系统调用跟踪。根据提示按步骤进行。

  • $U/_sysinfotest\添加到MakefileUPROGS
  • user/user.h中添加系统调用:
    struct sysinfo;
    int sysinfo(struct sysinfo*);
    
  • user/usys.pl中添加stub entry("sysinfo");
  • kernel/syscall.h中添加系统调用编号#define SYS_sysinfo 23
  • kernel/sysproc.c中添加函数sys_sysinfo(),代码如下,free_memory()proc_num()函数待实现:
    #include "sysinfo.h"
    
    uint64
    sys_sysinfo(void)
    {
        struct sysinfo info;
        uint64 addr;
    
        argaddr(0, &addr);
        struct proc* p = myproc();
        info.freemem = free_memory();
        info.nproc = proc_num();
        // 将内核态中的info复制到用户态
        if (copyout(p->pagetable, addr, (char*)&info, sizeof(info)) < 0)
            return -1;
        return 0;
    }
    
  • kernel/syscall.c中增加系统调用的函数指针:
    extern uint64 sys_sysinfo(void);
    
    static uint64 (*syscalls[])(void) = {
    [SYS_fork]    sys_fork,
    [SYS_exit]    sys_exit,
    [SYS_wait]    sys_wait,
    [SYS_pipe]    sys_pipe,
    [SYS_read]    sys_read,
    [SYS_kill]    sys_kill,
    [SYS_exec]    sys_exec,
    [SYS_fstat]   sys_fstat,
    [SYS_chdir]   sys_chdir,
    [SYS_dup]     sys_dup,
    [SYS_getpid]  sys_getpid,
    [SYS_sbrk]    sys_sbrk,
    [SYS_sleep]   sys_sleep,
    [SYS_uptime]  sys_uptime,
    [SYS_open]    sys_open,
    [SYS_write]   sys_write,
    [SYS_mknod]   sys_mknod,
    [SYS_unlink]  sys_unlink,
    [SYS_link]    sys_link,
    [SYS_mkdir]   sys_mkdir,
    [SYS_close]   sys_close,
    [SYS_trace]   sys_trace,
    [SYS_sysinfo] sys_sysinfo,
    };
    
  • kernel/proc.c中增加proc_num()函数,统计进程数,代码如下:
    int
    proc_num(void)
    {
        int i;
        int n = 0;
        for (i = 0; i < NPROC; i++)
        {
            if (proc[i].state != UNUSED) n++;
        }
        return n;
    }
    
  • kernel/kalloc.c中增加free_memory()函数,统计可用内存,代码如下:
    uint64 
    free_memory(void)
    {
        struct run* p = kmem.freelist;
        uint64 num = 0;
        while (p)
        {
            num ++;
            p = p->next;
        }
        return num * PGSIZE;
    }
    
  • kernel/defs.h中对应的区域增加函数声明,代码如下:
    uint64          free_memory(void);
    int             proc_num(void);
    

使用make GRADEFLAGS=sysinfo grade测试代码是否通过。

题目提示copyout()函数的使用可以参考filestat()函数,这个函数的功能是从内核复制到用户,传入参数依次为当前进程的页表、地址、需要复制内容的起始地址、需要复制的长度。

测试结果

使用make grade测试,结果如下:

== Test trace 32 grep ==
$ make qemu-gdb
trace 32 grep: OK (1.5s)
== Test trace all grep ==
$ make qemu-gdb
trace all grep: OK (1.0s)
== Test trace nothing ==
$ make qemu-gdb
trace nothing: OK (0.9s)
== Test trace children ==
$ make qemu-gdb
trace children: OK (8.3s)
== Test sysinfotest ==
$ make qemu-gdb
sysinfotest: OK (1.1s)
posted on 2024-02-14 19:03  未连接到互联网  阅读(35)  评论(0编辑  收藏  举报