MIT6.S081 Lab2-System Calls

这篇博客介绍了如何在XV6操作系统中添加新的系统调用,包括trace和sysinfo。trace系统调用用于追踪指定的系统调用,而sysinfo则用于获取系统的空闲内存和当前运行进程数。实验涵盖了从声明系统调用到实现功能的完整过程,包括修改头文件、添加入口、更新系统调用表、实现功能函数以及添加到Makefile。通过这些改动,用户可以在用户空间中获取系统内部信息,增强了XV6的调试和监控能力。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实验准备

  1. xv6 book第2章,第4章的4.3节和4.4节
  2. 系统调用的用户空间代码:user/user.h,user/usys.pl
  3. 系统调用的内核空间代码:kernel/syscall.h,kernel/syscall.c
  4. 进程相关代码:kernel/proc.h,kernel/proc.c

切换分支

$ git fetch
$ git checkout syscall
$ make clean

SYstem call tracing

添加1个系统调用trace,辅助完成后续实验的调试。该系统调用传入1个参数:整数mask,它的位用来指定要跟踪的系统调用

流程图

在这里插入图片描述

实验流程与代码

1.声明系统调用

user/user.h中,添加int trac(int)

// 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(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);
2.添加entry

user/usys.pl中,添加entry("trace")

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");
3.添加系统调用编号

kernel/syscall.h中,添加#define SYS_trace 22

// 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
4.添加全局声明

kernel/syscall.c中,添加extern uint64 sys_trace(void);

extern uint64 sys_chdir(void);
extern uint64 sys_close(void);
extern uint64 sys_dup(void);
extern uint64 sys_exec(void);
extern uint64 sys_exit(void);
extern uint64 sys_fork(void);
extern uint64 sys_fstat(void);
extern uint64 sys_getpid(void);
extern uint64 sys_kill(void);
extern uint64 sys_link(void);
extern uint64 sys_mkdir(void);
extern uint64 sys_mknod(void);
extern uint64 sys_open(void);
extern uint64 sys_pipe(void);
extern uint64 sys_read(void);
extern uint64 sys_sbrk(void);
extern uint64 sys_sleep(void);
extern uint64 sys_unlink(void);
extern uint64 sys_wait(void);
extern uint64 sys_write(void);
extern uint64 sys_uptime(void);
extern uint64 sys_trace(void);
5.添加系统调用表

kernel/syscall.c中,添加[SYS_trace] sys_trace,

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,
};
6.实现trace的功能
添加系统调用追踪号

kernel/proc.h中,添加int mask

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

  // p->lock must be held when using these:
  enum procstate state;        // Process state
  struct proc *parent;         // Parent process
  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

  // 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)
  int mask;                    // 添加一个系统调用的追踪号
};
实现sys_trace
//kernel/sysproc.c
uint64 sys_trace(void){
  int n;
  //argint()用于读取在a0-a5寄存器中传递的系统调用参数
  if(argint(0, &n)<0){
    return -1;
  }
  //myproc()函数获取当前进程的struct proc,即PCB
  myproc()->mask=n;
  return 0;
}
fork时,子进程与父进程有同样的mask
// kernel/proc.c


// Create a new process, copying the parent.
// Sets up child kernel stack to return as if from fork() system call.
int
fork(void)
{
  int i, pid;
  struct proc *np;
  struct proc *p = myproc();

  // Allocate process.
  if((np = allocproc()) == 0){
    return -1;
  }

  // Copy user memory from parent to child.
  if(uvmcopy(p->pagetable, np->pagetable, p->sz) < 0){
    freeproc(np);
    release(&np->lock);
    return -1;
  }
  np->sz = p->sz;

  np->parent = p;

  // copy saved user registers.
  *(np->trapframe) = *(p->trapframe);

  // Cause fork to return 0 in the child.
  np->trapframe->a0 = 0;

  // increment reference counts on open file descriptors.
  for(i = 0; i < NOFILE; i++)
    if(p->ofile[i])
      np->ofile[i] = filedup(p->ofile[i]);
  np->cwd = idup(p->cwd);

  safestrcpy(np->name, p->name, sizeof(p->name));

  np->mask = p->mask;   // 子进程的trace_mask = 父进程的trace_mask

  pid = np->pid;

  np->state = RUNNABLE;

  release(&np->lock);

  return pid;
}
输出trace

建立系统调用字典,方便寻找

// kernel/syscall.c

static char *syscall_names[] = {"", "fork", "exit", "wait", "pipe",
"read", "kill", "exec", "fstat", "chdir", "dup", "getpid", "sbrk", 
"sleep", "uptime", "open", "write", "mknod", "unlink", "link",
"mkdir", "close", "trace"};

syscall中,输出trace

// kernel/syscall.c

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]();
    if(p->mask && ((p->mask >> num) & 1)){
      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;
  }
}
添加到Makefile

user/trace.c添加到Makefile

$U/_trace
7.结果显示
  1. make qemu

  2. trace 32 grep hello RADEME

在这里插入图片描述

Sysinfo

加1个系统调用,sysinfo用来收集当前系统的相关信息。

sysinfo传入1个参数:指向struct sysinfo的指针(user/sysinfo.h);其中freemen表示空闲内存的字节数,nproc表示当前进程状态不是UNUSED的进程数;

提供了1个测试程序:sysinfotest,如果打印"sysinfotest:OK",证明完成

实验流程与代码

1.声明系统调用与结构体

user/user.h中,声明系统调用和结构体

  • 声明结构体

    struct stat;
    struct rtcdate;
    struct sysinfo;
    
  • 声明系统调用

    // 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(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);
    int sysinfo(struct sysinfo*);
    
2. 添加entry

user/usys.pl中,添加entry("sysinfo")

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");
entry("sysinfo");

3. 添加系统调用编号

kernel/syscall.h中,添加#define SYS_sysinfo 23

// 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
#define SYS_sysinfo  23

4. 添加全局声明

kernel/syscall.c中,添加extern uint64 sys_sysinfo(void);

extern uint64 sys_chdir(void);
extern uint64 sys_close(void);
extern uint64 sys_dup(void);
extern uint64 sys_exec(void);
extern uint64 sys_exit(void);
extern uint64 sys_fork(void);
extern uint64 sys_fstat(void);
extern uint64 sys_getpid(void);
extern uint64 sys_kill(void);
extern uint64 sys_link(void);
extern uint64 sys_mkdir(void);
extern uint64 sys_mknod(void);
extern uint64 sys_open(void);
extern uint64 sys_pipe(void);
extern uint64 sys_read(void);
extern uint64 sys_sbrk(void);
extern uint64 sys_sleep(void);
extern uint64 sys_unlink(void);
extern uint64 sys_wait(void);
extern uint64 sys_write(void);
extern uint64 sys_uptime(void);
extern uint64 sys_trace(void);
extern uint64 sys_sysinfo(void);
5. 添加系统调用表

kernel/syscall.c中,添加[SYS_trace] sys_trace,

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,
};
6.实现sysinfo的功能
获取空闲内存

根据提示4,可以先去看源码kernel/kalloc.c

该函数为xv6的为物理内存分配器,物理内存分页管理,每页有PGSIZE(4096)字节

struct run {
  struct run *next;
};

struct {
  struct spinlock lock;
  struct run *freelist;	// 指向空闲页的链表
} kmem;

void
kfree(void *pa)
{
  struct run *r;

  if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)
    panic("kfree");

  // Fill with junk to catch dangling refs.
  memset(pa, 1, PGSIZE);

  r = (struct run*)pa;

  //释放内存,将内存放到链表头节点——前插法
  acquire(&kmem.lock);
  r->next = kmem.freelist;	
  kmem.freelist = r;
  release(&kmem.lock);
}

kernel/kalloc.c中添加cnt_free_mem函数统计空闲内存

// 计算空闲内存
uint64 cnt_free_mem(void){
  acquire(&kmem.lock);  //上锁
  uint64 free_mem_count = 0;

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

将该函数放入到kernel/defs.h中声明

// kalloc.c
void*           kalloc(void);
void            kfree(void *);
void            kinit(void);
uint64          cnt_free_mem(void);
获取运行中的线程数

根据提示4,可以先去看源码kernel/proc.c

struct proc proc[NPROC];	// 每个元素都代表1个进程的相关信息

kernel/proc.h中查看proc结构体的定义

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

  // p->lock must be held when using these:
  enum procstate state;        // Process state
  struct proc *parent;         // Parent process
  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

  // 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)
  int mask;
};

只需要遍历所有的进程,获取其状态信息,判断是不是UNUSED并统计数目

kernel/proc.c中,添加cnt_proc函数

// 计算进程数
uint64 cnt_proc(void){
  uint64 cnt = 0;
  for(struct proc *p = proc; p < &proc[NPROC]; p++){
    if(p->state != UNUSED) cnt++;
  }
  return cnt;
}

将该函数放入到kernel/defs.h中声明

// proc.c
int             cpuid(void);
void            exit(int);
int             fork(void);
int             growproc(int);
pagetable_t     proc_pagetable(struct proc *);
void            proc_freepagetable(pagetable_t, uint64);
int             kill(int);
struct cpu*     mycpu(void);
struct cpu*     getmycpu(void);
struct proc*    myproc();
void            procinit(void);
void            scheduler(void) __attribute__((noreturn));
void            sched(void);
void            setproc(struct proc*);
void            sleep(void*, struct spinlock*);
void            userinit(void);
int             wait(uint64);
void            wakeup(void*);
void            yield(void);
int             either_copyout(int user_dst, uint64 dst, void *src, uint64 len);
int             either_copyin(void *dst, int user_src, uint64 src, uint64 len);
void            procdump(void);
uint64          cnt_proc(void);
实现sys_sysinfo
#include "sysinfo.h"

uint64 sys_sysinfo(void){
  // 从用户态读入1个指针,作为存放susinfo的buffer
  uint64 addr;
  if(argaddr(0, &addr) < 0)
    return -1;

  // 定义1个sysinfo结构的变量sinfo, 记录系统调用的信息
  struct sysinfo sinfo;
  sinfo.freemem = cnt_free_mem(); // 计算空闲的字节数
  sinfo.nproc = cnt_proc(); // 计算并行的线程数

  // 复制 sinfo 的内容到用户态传来的地址
  // 使用copyout 结合当前进程的页表,获得进程传进来的指针(逻辑地址)对应的物理地址
  // 将sinfo中的数据 复制到 该指针所指向的位置,共用户进程使用
  if(copyout(myproc()->pagetable, addr, (char*)&sinfo, sizeof(sinfo)) < 0)
    return -1;
  return 0;
}
添加Makefile
$U/_sysinfotest

id procdump(void);
uint64 cnt_proc(void);


##### 实现sys_sysinfo

```c
#include "sysinfo.h"

uint64 sys_sysinfo(void){
  // 从用户态读入1个指针,作为存放susinfo的buffer
  uint64 addr;
  if(argaddr(0, &addr) < 0)
    return -1;

  // 定义1个sysinfo结构的变量sinfo, 记录系统调用的信息
  struct sysinfo sinfo;
  sinfo.freemem = cnt_free_mem(); // 计算空闲的字节数
  sinfo.nproc = cnt_proc(); // 计算并行的线程数

  // 复制 sinfo 的内容到用户态传来的地址
  // 使用copyout 结合当前进程的页表,获得进程传进来的指针(逻辑地址)对应的物理地址
  // 将sinfo中的数据 复制到 该指针所指向的位置,共用户进程使用
  if(copyout(myproc()->pagetable, addr, (char*)&sinfo, sizeof(sinfo)) < 0)
    return -1;
  return 0;
}
添加Makefile
$U/_sysinfotest
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值