6.S081-Lab2

本文介绍了如何在RISC-V架构上进行系统调用的追踪功能实现,包括在用户态和内核态添加代码,以及使用gdb进行调试。实验涉及添加新的trace系统调用,使用户能控制追踪特定系统调用,并在返回时打印相关信息。此外,还提到了sysinfo系统调用的添加,用于获取系统信息。
摘要由CSDN通过智能技术生成

system calls

参考书籍

实验地址

Using gdb

vscode debug:
https://www.cnblogs.com/KatyuMarisaBlog/p/13727565.html
https://pdos.csail.mit.edu/6.828/2022/labs/syscall.html

操作步骤:

新打开一个窗口在项目当前目录下:
make qemu-gdb
最后会得到一个端口号,
再打开一个新的窗口
riscv64-unknown-elf-gdb
target remote localhost:<PORT NUMBER FROM POINT 6 ABOVE>(刚才得到的端口)

// 所有内核的文件表向
file kernel/kernel
//比如你要断点ls的文件
file user/_ls
B 挂载断点
C 下一个断点
N 单步断点

System call tracing :

在本作业中,您将添加一个系统调用跟踪功能,该功能可能会在以后调试实验室时对您有所帮助。您将创建一个新的跟踪系统调用来控制跟踪。它应该有一个参数,一个整数“掩码”,其位指定要跟踪的系统调用。例如,要跟踪fork系统调用,程序调用trace(1<<SYS_fork),其中SYS_fork是kernel/syscall.h中的系统调用编号。如果系统调用的编号在掩码中设置,则必须修改xv6内核,以便在每个系统调用即将返回时打印出一行。该行应包含进程id、系统调用的名称和返回值;您不需要打印系统调用参数。跟踪系统调用应启用对调用它的进程及其随后派生的任何子进程的跟踪,但不应影响其他进程。

CPU model:
CPU can execute instructions: machine mode, supervisor mode, and user mode

主要就是要搞清楚需要在哪些地方添加代码,当你添加代码时:
Userspace
1、 makefile 新增 .c 调用
2、 user.h 声明函数
3、usys.pl 函数调用跳板:Makefile invokes the perl script user/usys.pl, which produces user/usys.S, the actual system call stubs。
Kernelspace
syscall.h 声明 kernel函数编号
syscall.c. 添加配置
sysproc.c 添加函数具体实现

±----------------+
| User Process |
±----------------+
|
|
V
±----------------+
| System Call |
| Wrapper |
±----------------+
|
|
V
±----------------+
| usys.S |
| Assembly |
±----------------+
|
|
V
±----------------+
| syscall() |
| in syscall.c |
±----------------+
|
|
V
±----------------+
| Kernel |
| Function |
±----------------+
|
|
V
±----------------+
| System Call |
| Interface |
±----------------+

makefile

UPROGS=\
	$U/_cat\
	$U/_echo\
	$U/_forktest\
	$U/_grep\
	$U/_init\
	$U/_kill\
	$U/_ln\
	$U/_ls\
	$U/_mkdir\
	$U/_rm\
	$U/_sh\
	$U/_stressfs\
	$U/_usertests\
	$U/_grind\
	$U/_wc\
	$U/_zombie\
	$U/_trace\

user.h
新增 trace 函数

// system calls
int fork(void);
int exit(int) __attribute__((noreturn));
int wait(int*);
int pipe(int*);
int write(int, const void*, int);
int read(int, void*, int);
int close(int);
int kill(int);
int exec(const char*, char**);
int open(const char*, int);
int mknod(const char*, short, short);
int unlink(const char*);
int fstat(int fd, struct stat*);
int link(const char*, const char*);
int mkdir(const char*);
int chdir(const char*);
int dup(int);
int getpid(void);
char* sbrk(int);
int sleep(int);
int uptime(void);
int trace(int);

user.h
新增 trace 函数,提供给用户调用

// system calls
int fork(void);
int exit(int) __attribute__((noreturn));
int wait(int*);
int pipe(int*);
int write(int, const void*, int);
int read(int, void*, int);
int close(int);
int kill(int);
int exec(const char*, char**);
int open(const char*, int);
int mknod(const char*, short, short);
int unlink(const char*);
int fstat(int fd, struct stat*);
int link(const char*, const char*);
int mkdir(const char*);
int chdir(const char*);
int dup(int);
int getpid(void);
char* sbrk(int);
int sleep(int);
int uptime(void);
int trace(int);

user/usys.pl 用户态trap进内核跳板,(makefile 会生成 user/usys.S, RISC-V ecall 进trap)

// system calls
	
entry("fork");
entry("exit");
entry("wait");
entry("pipe");
entry("read");
entry("write");
entry("close");
entry("kill");
entry("exec");
entry("open");
entry("mknod");
entry("unlink");
entry("fstat");
entry("link");
entry("mkdir");
entry("chdir");
entry("dup");
entry("getpid");
entry("sbrk");
entry("sleep");
entry("uptime");
entry("trace");

内核态

系统调用映射
kernel/syscall.h 新增trace

// System call numbers
#define SYS_fork    1
#define SYS_exit    2
#define SYS_wait    3
#define SYS_pipe    4
#define SYS_read    5
#define SYS_kill    6
#define SYS_exec    7
#define SYS_fstat   8
#define SYS_chdir   9
#define SYS_dup    10
#define SYS_getpid 11
#define SYS_sbrk   12
#define SYS_sleep  13
#define SYS_uptime 14
#define SYS_open   15
#define SYS_write  16
#define SYS_mknod  17
#define SYS_unlink 18
#define SYS_link   19
#define SYS_mkdir  20
#define SYS_close  21
#define SYS_trace  22

系统调用实现
kernel/sysproc.c

uint64
sys_trace(void)
{
  int traceid;
   argint(0, &traceid);
   myproc()->mask=traceid;
  return traceid;
}

给进程新增一个mask 用于记录它的traceid
kernel/proc.h

// Per-process state
struct proc {
  struct spinlock lock;

  // p->lock must be held when using these:
  enum procstate state;        // Process state
  void *chan;                  // If non-zero, sleeping on chan
  int killed;                  // If non-zero, have been killed
  int xstate;                  // Exit status to be returned to parent's wait
  int pid;                     // Process ID
  uint64 mask;                    // trace Id
  // wait_lock must be held when using this:
  struct proc *parent;         // Parent process

  // these are private to the process, so p->lock need not be held.
  uint64 kstack;               // Virtual address of kernel stack
  uint64 sz;                   // Size of process memory (bytes)
  pagetable_t pagetable;       // User page table
  struct trapframe *trapframe; // data page for trampoline.S
  struct context context;      // swtch() here to run process
  struct file *ofile[NOFILE];  // Open files
  struct inode *cwd;           // Current directory
  char name[16];               // Process name (debugging)
};

子进程也需要传递该trace id

// Create a new process, copying the parent.
// Sets up child kernel stack to return as if from fork() system call.
int
fork(void)
  np->mask=p->mask;

在系统调用入口打印
kernel/syscall.c

// Prototypes for the functions that handle system calls.
extern uint64 sys_trace(void);
// An array mapping syscall numbers from syscall.h
// to the function that handles the system call.
static uint64 (*syscalls[])(void) = {
[SYS_trace]   sys_trace,
//新增名字映射
const char *syscall_names[] = {
[SYS_fork]    "fork",
[SYS_exit]    "exit",
[SYS_wait]    "wait",
[SYS_pipe]    "pipe",
[SYS_read]    "read",
[SYS_kill]    "kill",
[SYS_exec]    "exec",
[SYS_fstat]   "fstat",
[SYS_chdir]   "chdir",
[SYS_dup]     "dup",
[SYS_getpid]  "getpid",
[SYS_sbrk]    "sbrk",
[SYS_sleep]   "sleep",
[SYS_uptime]  "uptime",
[SYS_open]    "open",
[SYS_write]   "write",
[SYS_mknod]   "mknod",
[SYS_unlink]  "unlink",
[SYS_link]    "link",
[SYS_mkdir]   "mkdir",
[SYS_close]   "close",
[SYS_trace]   "trace",
[SYS_sysinfo] "sysinfo",
};

//修改syscall 当trace id 等于 syscall id 的时候就打印
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->mask & (1<<num) ){
      printf("%d: syscall %s -> %d\n",p->pid, syscall_names[num], p->trapframe->a0);
    }
  } else {
    printf("%d %s: unknown sys call %d\n",
            p->pid, p->name, num);
    p->trapframe->a0 = -1;
  }
}

Sysinfo

在这个分配中,您将添加一个系统调用sysinfo,用于收集有关正在运行的系统的信息。系统调用采用一个参数:指向结构sysinfo的指针(请参阅kernel/sysinfo.h)。内核应该填写此结构的字段:freemem字段应该设置为可用内存的字节数,nproc字段应该设为状态不是UNUSED的进程数。我们提供一个测试程序sysinfotest;如果它打印出“sysinfotest:OK”,那么您就通过了这个赋值。

系统调用过程: 添加sysinfo相关
Userspace
1、 makefile 新增 .c 调用
2、 user.h 声明函数
3、usys.pl 函数调用跳板:Makefile invokes the perl script user/usys.pl, which produces user/usys.S, the actual system call stubs。
Kernelspace
syscall.h 声明 kernel函数编号
syscall.c. 添加配置
sysproc.c 添加函数具体实现

kalloc.c
在手册后续中会告诉你 空闲的空间通过 freelist 管理,这里参考kalloc 函数

uint64 get_last_mem(void){
  acquire(&kmem.lock);

    uint64  last_mem=0;
   struct run *r=kmem.freelist;
  while(r){
    last_mem+=PGSIZE;
    r=r->next;
  }
  release(&kmem.lock);

  printf("last_mem:%d \n",last_mem);
  return last_mem;
}

在proc.c中添加,具体参考procdump

uint64 
get_active_process_num(void)
{
  struct proc *p;
    uint64 cnt=0;
  for(p = proc; p < &proc[NPROC]; p++) {
      if(p->state != UNUSED){
        cnt++;
      }
  }
  return cnt;
}

sys_pro.c
新增sys_sysinfo,这里 copyout(p->pagetable, addr, (char *)&info, sizeof(info)) 的使用具体可以参考file.c 和函数定义

uint64
sys_sysinfo(void)
{

uint64 addr; // user pointer to arg addr

 argaddr(0, &addr);

struct sysinfo  info;
struct proc *p = myproc();

  info.freemem=get_last_mem();
  info.nproc=get_active_process_num();

    if(copyout(p->pagetable, addr, (char *)&info, sizeof(info)) < 0)
      return -1;

  return 0;

}

总结

对于lab2 需要学习到,一次从用户态到内核态的调用过程是怎么样执行的,怎么用gdb去分析内存代码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值