前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >6.S081/6.828: 2 Lab system calls

6.S081/6.828: 2 Lab system calls

原创
作者头像
冰寒火
修改2022-11-26 05:02:54
5160
修改2022-11-26 05:02:54
举报
文章被收录于专栏:软件设计软件设计

上一个Lab实现了一些shell工具,这个Lab实现一些系统调用,来帮助理解系统调用如何工作、如何暴露。

一、trace

1 问题分析

实现一个trace,能够追踪某个进程(包括子进程)指定系统调用执行情况,打印它的状态,如下图所示。

image.png
image.png
  1. 传递mask来表示追踪的是哪个系统调用,例如:mask=1<<SYS_fork。
  2. 打印进程及其子进程运行某个系统调用的日志,所以mask应该添加在进程结构体中,并且fork内核函数也需要copy mask字段。

主要步骤如下:

  1. kernel/proc.h添加tracemask字段。
  2. kernel/sysproc.c添加sys_trace函数。
  3. kernel/syscall.h添加系统调用号。
  4. kernel/syscall.c系统调用表中添加一项,并增加系统调用名称表。
  5. kernel/syscall.c中修改syscall函数。
  6. 在user/user.h中暴露该系统调用,在user/usys.pl中增加一项,用于生成汇编代码触发中断。

2 代码实现

代码语言:c
复制
//代码比较分散,只介绍主要代码
uint64
sys_trace(void)
{
  int mask;

  if(argint(0, &mask) < 0)
    return -1;
  myproc()->tracemask=mask;
  return 0;
}


static char* syscallnames[] = {
[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",
};
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->tracemask&(1<<num)){
      printf("%d: %s %s %s %d\n",p->pid,"syscall",syscallnames[num],"->",p->trapframe->a0);
    }
    
  } else {
    printf("%d %s: unknown sys call %d\n",
            p->pid, p->name, num);
    p->trapframe->a0 = -1;
  }
}

二、sysinfo

1 问题分析

sysinfo系统调用用来统计当前空闲物理内存、存在的进程数量,我们需要对内存分配器、进程管理有一定的了解。

  1. xv6的物理内存是用空闲链表管理的,统计这条链表上的页数就行。
  2. xv6的进程分配是采用一个进程数组来维护,直接遍历这个数组上非UNUSED状态的进程即可。
  3. 用户态向内核传递数据,会存放在寄存器中,而内核trap代码会将寄存器保存在进程trap frame中。内核中有argint、argaddr、argfd,支持读取整数、指针、文件描述符,都是argraw的封装。
  4. 内核态和用户态不会直接读写同一份数据,而是会拷贝,copyout是将数据拷贝到用户地址中。

2 内存分配和进程管理

代码语言:c
复制
struct run {
  struct run *next;
};

struct {
  struct spinlock lock;
  struct run *freelist;
} kmem;


struct cpu cpus[NCPU];
//进程管理数组
struct proc proc[NPROC];

enum procstate { UNUSED, USED, SLEEPING, RUNNABLE, RUNNING, ZOMBIE };

每个run表示一个物理页,它的指针就是物理页起始地址,每一物理页都会存放下一物理页的地址,来减少辅助空间。进程是采用一个数组来管理。

3 代码实现

代码语言:c
复制
uint64
sys_sysinfo(void)
{
  struct sysinfo info;
  info.freemem=count_free_memory();
  info.nproc=count_proc();

  uint64 addr;
  if(argaddr(0, &addr) < 0)
    return -1;
  if(copyout(myproc()->pagetable,addr,(char*)&info,sizeof info)<0){
    return -1;
  }
  return 0;
}


uint64 count_free_memory(void){
  int pageNum=0;
  struct run *r;
  acquire(&kmem.lock);
  r=kmem.freelist;
  while (r){
    pageNum++;
    r=r->next;
  }
  release(&kmem.lock);
  return pageNum*PGSIZE;
}

//计算进程数量
uint64 count_proc(void){
  int count=0;
  for(int i=0;i< NPROC ;i++){
    if(proc[i].state!=UNUSED){
      count++;
    }
  } 
  return count;
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、trace
    • 1 问题分析
      • 2 代码实现
      • 二、sysinfo
        • 1 问题分析
          • 2 内存分配和进程管理
            • 3 代码实现
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
            http://www.vxiaotou.com