MIT6.S081 - Lab2: system calls

这篇具有很好参考价值的文章主要介绍了MIT6.S081 - Lab2: system calls。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Lab2: system calls

预备知识

执行一次系统调用的流程:

USER MODE

step1:系统调用声明

  • user/user.h:系统调用函数(如 int fork(void))

step2:ecall 进入内核态

  • user/usys.S(该文件由 user/usys.pl 生成,后续添加函数可以在这里添加):执行如下命令
 .global fork
 fork:
   li a7, SYS_fork
   ecall
   ret
  • 将系统调用的编号(在kernel/syscall.h中定义)写入a7寄存器
  • ecall进入中断处理函数

KERNEL MODE

step3:保存数据并跳转到中断判断函数

  • kernel/trampoline.S:在uservec中保存寄存器、切换到内核栈、切换栈指针SP等,最后跳转到内核指定的中断判断函数usertrap(在kernel/trap.c中)
    • 每一个进程都对应一个状态结构体proc(在kernel/proc.h中定义这个结构体),将数据存储在这里

step4:中断判断函数

  • kernel/trap.c:中断判断函数usertrap用于处理来自用户态的中断、异常或系统调用,在此处判断是否是系统调用,如果是,则执行响应函数syscall

step5:执行对应的系统调用

  • kernel/syscall.c:响应函数syscall用于对号入座,根据给出的syscalls表(将系统调用编号与执行函数相对应)获取系统调用类型(系统调用编号从a7寄存器中读取),执行相应的函数(进入kernel/sysproc.c中);系统调用的返回值传递给了a0,后续会回传给用户态

step6:系统调用函数

  • kernel/sysproc.c:保存着系统调用的执行函数,如果系统调用有参数,需要用argraw(读取参数本质上是从内存中恢复,见kernel/syscall.c)取出保存的寄存器数据,然后函数最终都将调用相对应的执行函数(在kernel/proc.c中)

step7:系统调用核心功能

  • kernel/proc.c:这里的函数用于执行核心功能,如创建进程等

添加系统调用需要考虑的地方

  1. user/user.h中添加系统调用的函数声明,并在user中创建相应的系统调用(*.c文件)(和lab1类似)

  2. user/usys.pl中添加系统调用项

  3. kernel/syscall.h添加系统调用的编号

  4. kernel/syscall.c中的syscalls表中添加映射关系,指出需要执行的函数

  5. kernel/sysproc.ckernel/proc.c中实现系统调用的执行函数

Part1:System call tracing

实现功能

  1. 添加一个系统调用的trace功能,在命令前输入trace <mask>,能够打印出该命令使用的系统调用

  2. mask = 1 << SYS_name为一个整数,它能够指定跟踪哪个系统调用,SYS_name是来自kernel/syscall.h的一个系统调用号,mask可以等于1 << SYS_name | 1 << SYS_other_name

  3. 如果在输入命令时使用trace <mask>,则在使用指定系统调用后应该返回一行包括进程id、系统调用名称和返回值的打印信息

  4. trace要求能够跟踪进程以及进程派生出的子进程,且不影响其他进程

实验提示

  1. user/user.huser/usys.plkernel/syscall.h添加系统调用以及编号

  2. kernel/sysproc.c添加一个sys_trace函数实现新的系统调用,并在状态结构体proc中插入一个新的变量(对该进程进行跟踪的mask掩码),系统调用的执行函数参考kernel/sysproc.c

  3. 此外需要对kernel/proc.cfork函数进行修改,因为调用了trace系统调用,会在当前进程状态proc(在kernel/proc.h中)中修改mask掩码的设置,同时每次fork时也需要对相关的子进程同步相关的设置

  4. 需要修改kernel/syscall.c下的syscall使其打印追踪信息;为了打印系统调用名称,需要额外创建字符串数组

实验代码

  1. user/user.h中添加int trace(int);的声明(trace接受一个整型的参数mask

  2. user/usys.pl中添加entry("trace");,这是进入内核态的入口

  3. Makefile中的UPROGS添加_trace

  4. 进入内核态,在kernel/syscall.h添加系统调用的编号#define SYS_trace 22

  5. kernel/syscall.c中添加系统调用的映射extern uint64 sys_trace(void);[SYS_trace] sys_trace

  6. kernel/proc.h中的proc中添加int mask

  7. kernel/sysproc.c中添加进入系统调用的函数

uint64
sys_trace(void){   // 参考sys_wait函数
  uint64 p;
  if(argaddr(0, &p) < 0)   // 获取trace的参数(只有一个)
    return -1;    
  return trace(p);  // 系统调用的执行函数
}
  1. kernel/proc.c 实现 trace 的系统调用执行函数(仅仅是进行一个掩码的赋值)
int 
trace(int mask){
  struct proc *p = myproc();
  p->mask = mask;     // 将掩码赋值给结构体的mask 
  return 0;
}
  1. kernel/defs.h(这个里面包含了 kernel 中常用函数的原型声明)中加入函数的声明 int trace(int)
  2. kernel/proc.c 中的 fork 函数中加一行代码 np->mask = p->mask;,将父进程的 mask 赋值给子进程的 mask
  3. kernel/syscall.c 中对 syscall 进行修改,打印追踪信息;并且为了打印系统调用的名称,创建一个字符串数组
char 
*sys_name[] = {
"",      "fork",  "exit",   "wait",   "pipe",  "read",  "kill",   "exec",
"fstat", "chdir", "dup",    "getpid", "sbrk",  "sleep", "uptime", "open",
"write", "mknod", "unlink", "link",   "mkdir", "close", "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->mask >> num & 1){   // 判断是否有mask输入
      // 打印进程id、系统调用的名称和返回值
      printf("%d: syscall %s -> %d", p->pid, sys_name[num], p->trapframe->a0);   // 这里注意使用的prinrf是因为xv6的kernel中专门定义了一个printf的函数,在Linux内核中只能使用prinrk打印
    }
  } else {
    printf("%d %s: unknown sys call %d\n",
            p->pid, p->name, num);
    p->trapframe->a0 = -1;
  }
}

p->mask >> num & 1:p->mask 为 1<<SYS_read,num 为从 a7 寄存器中读出来的 SYS_read,p->mask >> num 能够将 mask 还原为 SYS_read,如果没有 mask,则不打印

实验感悟

  1. 在阅读源码的过程中,看到有些地方涉及到汇编语言,之后要把这块给学习一下,要不然很影响阅读体验
  2. 虽然按照系统调用的步骤能把整个流程走下来,但是每个函数以及它们之间的关系实际上还没有特别清楚,需要再进行梳理(将每个函数的作用都搞清楚)
  3. 在进行 proc 修改的时候,要考虑到 fork 函数的子进程是否需要复制参数

Part2: Sysinfo

实现功能

  1. 添加一个系统调用 sysinfo,通过系统调用将收集正在运行的程序的信息
  2. 在这个系统调用中将传入一个结构体指针 sysinfo,结构体定义在 kernel/sysinfo.h 中,其中 freemem 应该设置为空闲内存的字节数,nproc 应该设置为进程状态不为 UNUSED 的进程数

实验提示

  1. user/user.h 中声明 sysinfo() 的原型,你需要预先声明结构体 sysinfo 的存在:struct sysinfo;int sysinfo(struct sysinfo *);
  2. sysinfo 需要将 struct sysinfo 复制回用户空间;参考 sys_fstat() (kernel/sysfile.c)filestat() (kernel/file.c) 学习使用 copyout()
  3. 要收集空闲内存量,可以在 kernel/kalloc.c 中添加一个函数
  4. 要收集进程数,请在 kernel/proc.c 中添加一个函数

实验代码

  1. user/user.h 添加 sysinfo 结构体和函数的声明 struct sysinfo;int sysinfo(struct sysinfo *);
  2. 分别在 user/usys.plMakefilekernel/syscall.hkernel/syscall.c 执行与上一个实验一样的步骤
  3. kernel/kalloc.c 中实现空闲内存大小的查找
uint64 
getfreemen(void){    // 获取空闲内存数量
  struct run *rp;
  uint64 result = 0;
  acquire(&kmem.lock);  // 考虑到并发问题,上锁
  rp = kmem.freelist;
  while(rp){
    result += 1;
    rp = rp->next;
  }
  release(&kmem.lock);
  return result * PGSIZE;   //一个内存页的大小为PGSIZE(4096)
}
  1. kernel/proc.c 中实现进程数的统计
int 
getproc(void){
  struct proc *p;
  int result = 0;
  for(p = proc; p < &proc[NPROC]; p++){
    acquire(&p->lock);   //上锁
    if(p->state != UNUSED){
      result += 1;
    }
    release(&p->lock);
  }
  return result;
}

上述两个步骤都需要在 kernel/defs.h 中添加函数声明文章来源地址https://www.toymoban.com/news/detail-856345.html

  1. kernel/sysproc.c 中实现执行函数 sys_sysinfo 的功能,记得添加 #include "sysinfo.h" 来使用 sysinfo 结构体
uint64   
sys_sysinfo(void){   
  struct sysinfo info;
  struct proc *p;
  uint64 addr;
  if(argaddr(0, &addr) < 0)    // 获取系统的指针参数
    return -1;
  p = myproc();
  info.freemem = getfreemen();
  info.nproc = getproc();
  // 从内核copy到用户
  if(copyout(p->pagetable, addr, (char *)&info, sizeof(info)) < 0)  //参考sys_fstat(在kernel/sysfile.c中)
    return -1;                
  return 0;
}

实验感悟

  1. 这个实验实现的内存大小和进程数的统计函数需要了解了 kalloc.c 和 proc.c 的函数后才能写出来,我两个文件看的还不太完全,之后需要再看看
  2. 这个实验中也使用了指针,这一块使用还不太熟练,C 语言还需要精进

到了这里,关于MIT6.S081 - Lab2: system calls的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • MIT6.S081学习笔记--lec 1

    abstract H/W 抽象化硬件 multiplex 多路复用 isolation 隔离性 sharing 共享(进程通信,数据共享) security / access control 安全性/权限控制 performance 性能/内核开销 range of applications 多应用场景 操作系统应该提供的功能:1. 多进程支持 2. 进程间隔离 3. 受控制的进程间通信 xv6 :一种在本

    2024年02月16日
    浏览(29)
  • MIT6.S081 - Lecture1: Introduction and Examples

    理解操作系统的设计和实现 通过 XV6 操作系统动手实验,可以扩展或改进操作系统 Abstraction: 对硬件进行抽象 Multiplex: 在多个应用程序之间共用硬件资源 Isolation: 隔离性,程序出现故障时,不同程序之间不能相互干扰 Sharing: 实现共享,如数据交互或协同完成任务 Securi

    2024年04月15日
    浏览(40)
  • mit6.828 - lab5笔记(上)

    unix的文件系统相关知识 unix将可用的磁盘空间划分为两种主要类型的区域: inode区域 和 数据区域 。 unix为每个文件分配一个inode,其中保存文件的 关键元数据 ,如文件的stat属性和指向文件数据块的指针。 数据区域中的空间会被分成大小相同的数据块(就像内存管理中的分

    2024年02月02日
    浏览(23)
  • mit s0681 lab2 Trace系统调用实现

    实验一 实现一个用户级别的程序,功能为,指定系统调用后,跟踪程序的系统调用情况 分析实验 实验目标为实现一个程序去跟踪指定程序的系统调用。因此目标有两个 实现一个程序 跟踪目标程序的系统调用 实现1,就需要在用户这边实现一个trace的相关程序,接收监控的

    2024年02月11日
    浏览(28)
  • MIT 6.S081 Lab Three

    本文为 MIT 6.S081 2020 操作系统 实验三解析。 MIT 6.S081课程前置基础参考: 基于RISC-V搭建操作系统系列 在本实验中,您将探索页表并对其进行修改,以简化将数据从用户空间复制到内核空间的函数。 开始编码之前,请阅读xv6手册的第3章和相关文件: * kernel/memlayout.h* ,它捕获了

    2024年02月09日
    浏览(33)
  • MIT6.828/6.S081 Mac OS下搭建xv6和risc-v

    题外话: 其实我是一名非计算机专业的在校生,因为对软件开发和服务器开发很感兴趣,并且这方面的就业相对我来说资源比较充沛,所以就学习了mit6.828的实验 课程的学习直接跟着官网的schedule走就行,先看Lecture下提供的讲义和手册,然后完成相应的Lab,Lab共计10个,主要

    2024年03月09日
    浏览(27)
  • 【MIT 6.S081】Lab7: Multithreading

    本Lab比较简单,就是为xv6添加一个用户级的多线程功能,然后熟悉一下Linux下多线程编程。 笔者用时约2h 这一部分的代码不涉及内核代码,所以也比较简单,根据提示修改 user/uthread.c 中的代码即可。仿照内核中进程转换函数 swtch 的实现即可。首先,添加一个 context 上下文结

    2023年04月09日
    浏览(25)
  • MIT6.5830 Lab1-GoDB实验记录(五)

    完成了Exercise 1,还有四个Exercise在等着我,慢慢来吧。 实验准备 了解缓冲池 缓冲池,俗称BP。相关的概念还有数据页和缓存页。页(Pages)的概念和操作系统中“分页”的概念是一样的,指的都是把逻辑地址空间分为若干同等大小的页,并从0开始编号。 而缓冲池(Buffer Po

    2024年02月05日
    浏览(36)
  • MIT6.5830 Lab1-GoDB实验记录(四)

    标签:Golang 读写缓冲区我是一点思路都没有,所以得单独开篇文章记录。 实验补充 了解buffer、序列化与反序列化 这里的序列化,简单来说类似于把一个很长的字符串拆成一个个字符;反序列化就是把这一个个字符拼回成完整的字符串。此处我们需要根据所给的Tuple,转换为

    2024年02月06日
    浏览(35)
  • 「实验记录」MIT 6.824 Raft Lab2C Persist

    MIT-6.824 2020 课程官网 Lab2: Raft 实验主页 simviso 精品付费翻译 MIT 6.824 课程 Paper - Raft extended version source code 的 Gitee 地址 Lab2C: Persist 的 Gitee 地址 课程官网提供的 Lab 代码下载地址,我没有访问成功,于是我从 Github 其他用户那里 clone 到干净的源码,有需要可以访问我的 Gitee 获取

    2024年02月08日
    浏览(64)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包