Mit6.828/6.S081 fall 2019的Lab1是Unix utilities,主要内容为利用xv6的系统调用实现sleep、pingpong、primes、find和xargs等工具。本文对各程序的实现思路及xv6的系统调用流程进行详细介绍。

前言

在实验之前,推荐阅读一下官网LEC1中提供的资料。其中Introduction是对该课程的的概述,examples则是几个系统编程的样例,这两部分快速浏览一遍即可。对于xv6 book的第一章,则建议稍微细致地阅读一遍,特别是对fork()、exec()、pipe()、dup()这几个系统调用的介绍,会在后面实验中用到。

实验环境搭建参考上一篇文章。进入xv6-riscv-fall19项目后可以看到两个比较重要的目录:kernel为xv6内核源码,里面除了os工作的核心代码(如进程调度),还有向外提供的接口(system call);user中则是用户程序,如我们熟悉的ls,echo命令等。本次实验的目的就是在user中增加用户程序,借助kernel中提供的system call来实现所需的功能。

实验思路

每一个Lab需要在对应的分支编写代码,进入xv6-riscv-fall19目录下,使用git checkout util切换到util分支,即可开始编写我们的程序。下面主要提供实现思路,具体实验代码请参考Github

实验完成后使用make grade可以执行单元测试进行评分,会以gdb-server模式启动qemu,并在gradelib.py中模拟gdb-client对我们的程序进行测试。如果在make grade时报错Timeout! Failed to connect to QEMU,可以将gradelib.py的325行改为self.sock.connect(("127.0.0.1", port))

sleep

sleep功能为使进程睡眠若干个时钟周期(xv6中一个tick为100ms),首先创建user/sleep.c源文件,引入user.h头文件,系统调用和工具函数都定义在该文件里。核心代码如下:

sleep(atoi(argv[1]));

完成编写后,在Makefile的UPROGS中追加一行$U/_sleep\。输入make qemu进行编译,成功后进入shell,输入sleep 10,如果进程睡眠了大约1s,则表示程序编写正确。

pingpong

功能是父进程通过管道向子进程发送1字节,子进程收到后向父进程回复1字节。

由于管道是单向流动的,所以两次调用pipe()创建两个管道,分别对应两个方向。使用fork()创建子进程,在子进程中先从管道1read()再向管道2write(),父进程中则与之相反。

primes

primes的功能是输出2~35之间的素数,实现方式是递归fork进程并使用管道链接,形成一条pipeline来对素数进行过滤。

每个进程收到的第一个数p一定是素数,后续的数如果能被p整除则之间丢弃,如果不能则输出到下一个进程,详细介绍可参考文档。伪代码如下:

void primes() {
p = read from left // 从左边接收到的第一个数一定是素数
if (fork() == 0):
primes() // 子进程,进入递归
else:
loop:
n = read from left // 父进程,循环接收左边的输入
if (p % n != 0):
write n to right // 不能被p整除则向右输出
}

还需要注意两点:

  • 文件描述符溢出: xv6限制fd的范围为0~15,而每次pipe()都会创建两个新的fd,如果不及时关闭不需要的fd,会导致文件描述符资源用尽。这里使用重定向到标准I/O的方式来避免生成新的fd,首先close()关闭标准I/O的fd,然后使用dup()复制所需的管道fd(会自动复制到序号最小的fd,即关闭的标准I/O),随后对pipe两侧fd进行关闭(此时只会移除描述符,不会关闭实际的file对象)。

  • pipeline关闭: 在完成素数输出后,需要依次退出pipeline上的所有进程。在退出父进程前关闭其标准输入fd,此时read()将读取到eof(值为0),此时同样关闭子进程的标准输入fd,退出进程,这样进程链上的所有进程就可以退出。

find

find功能是在目录中匹配文件名,实现思路是递归搜索整个目录树。

使用open()打开当前fd,用fstat()判断fd的type,如果是文件,则与要找的文件名进行匹配;如果是目录,则循环read()到dirent结构,得到其子文件/目录名,拼接得到当前路径后进入递归调用。注意对于子目录中的...不要进行递归。

xargs

xargs的功能是将标准输入转为程序的命令行参数。可配合管道使用,让原本无法接收标准输入的命令可以使用标准输入作为参数。

根据lab中的使用例子可以看出,xv6的xargs每次回车都会执行一次命令并输出结果,直到ctrl+d时结束;而linux中的实现则是一直接收输入,收到ctrl+d时才执行命令并输出结果。

思路是使用两层循环读取标准输入:

  • 内层循环依次读取每一个字符,根据空格进行参数分割,将参数字符串存入二维数组中,当读取到'\n'时,退出当前循环;当接收到ctrl+d(read返回的长度<0)时退出程序。
  • 外层循环对每一行输入fork()出子进程,调用exec()执行命令。注意exec接收的二维参数数组argv,第一个参数argv[0]必须是该命令本身,最后一个参数argv[size-1]必须为0,否则将执行失败。

xv6系统调用流程

Lab中对system call的使用很简单,看起来和普通函数调用并没有什么区别,但实际上的调用流程是较为复杂的。我们很容易产生一些疑问:系统调用的整个生命周期具体是什么样的?用户进程和内核进程之间是如何切换上下文的?系统调用的函数名、参数和返回值是如何在用户进程和内核进程之间传递的?

1.用户态调用

在用户空间,所有system call的函数声明写在user.h中,调用后会进入usys.S执行汇编指令:将对应的系统调用号(system call number)置于寄存器a7中,并执行ecall指令进行系统调用,其中函数参数存在a0~a5这6个寄存器中。ecall指令将触发软中断,cpu会暂停对用户程序的执行,转而执行内核的中断处理逻辑,陷入(trap)内核态。

2.上下文切换

中断处理在kernel/trampoline.S中,首先进行上下文的切换,将user进程在寄存器中的数据save到内存中(保护现场),并restore(恢复)kernel的寄存器数据。内核中会维护一个进程数组(最多容纳64个进程),存储每个进程的状态信息,proc结构体定义在proc.h,这也是xv6对PCB(Process Control Block)的实现。用户程序的寄存器数据将被暂时保存到proc->trapframe结构中。

3.内核态执行

完成进程切换后,调用trap.c/usertrap(),接着进入syscall.c/syscall(),在该方法中根据system call number拿到数组中的函数指针,执行系统调用函数。函数参数从用户进程的trapframe结构中获取(a0~a5),函数执行的结果则存储于trapframe的a0字段中。完成调用后同样需要进程切换,先save内核寄存器到trapframe->kernel_*,再将trapframe中暂存的user进程数据restore到寄存器,重新回到用户空间,cpu从中断处继续执行,从寄存器a0中拿到函数返回值。

至此,系统调用完成,共经历了两次进程上下文切换:用户进程 -> 内核进程 -> 用户进程,同时伴随着两次CPU工作状态的切换:用户态 -> 内核态 -> 用户态。

实验代码:https://github.com/zhayujie/xv6-riscv-fall19

原文链接:https://zhayujie.com/mit6828-lab-util.html

MIT6.S081/6.828 实验1:Lab Unix Utilities的更多相关文章

  1. 【MIT6.S081/6.828】手把手教你搭建开发环境

    目录 1. 简介 2. 安装ubuntu20.04 3. 更换源 3.1 更换/etc/apt/sources.list文件里的源 3.2 备份源列表 3.3 打开sources.list文件修改 3 ...

  2. 【翻译】【中英对照】【企业库6】动手实验 Hands-On Lab 日志应用程序块索引页

    Logging Application Block Hands-On Lab for Enterprise Library 企业库的日志应用程序块动手实验 This walkthrough shoul ...

  3. CS:APP配套实验 Data Lab

    刚刚完成注册博客,想写一篇随笔,方便以后自己回顾.如果恰好也能帮助到你,是我的荣幸. 这次随笔是记载我的计算机系统(CS:APP,Computer Systems:A Programer's Pers ...

  4. 深入理解计算机系统 (CS:APP) - 高速缓存实验 Cache Lab 解析

    原文地址:https://billc.io/2019/05/csapp-cachelab/ 这个实验是这学期的第四个实验.作为缓存这一章的配套实验,设计得非常精妙.难度上来讲,相比之前的修改现成文件, ...

  5. 深入理解计算机系统 (CS:APP) 缓冲区漏洞实验 – Buffer Lab 解析

    原文地址:https://billc.io/2019/05/csapp-cachelab/ 写在前面 这是 CSAPP 官网上的第 4 个实验 buflab,也是学校要求的第三个实验.这个实验比上一个 ...

  6. MIT 6.828 JOS学习笔记0. 写在前面的话

    0. 简介 操作系统是计算机科学中十分重要的一门基础学科,是一名计算机专业毕业生必须要具备的基础知识.但是在学习这门课时,如果仅仅把目光停留在课本上一些关于操作系统概念上的叙述,并不能对操作系统有着深 ...

  7. XV6学习(1) Lab util

    正在学习MIT的6.S081,把做的实验写一写吧. 实验的代码放在了Github上. 第一个实验是Lab util,算是一个热身的实验,没有涉及到系统的底层,就是使用系统调用来完成几个用户模式的小程序 ...

  8. ChCore Lab1 机器启动 实验笔记

    本文为上海交大 ipads 研究所陈海波老师等人所著的<现代操作系统:原理与实现>的课程实验(LAB)的学习笔记的第一篇. 书籍官网:现代操作系统:原理与实现,里面有实验的参考指南和代码仓 ...

  9. Acadia Lab 203 + Lab 231

    在做完 Lab 6 之后,惊觉选做实验缺口很大,于是遍历了一遍夏任务,找到了一条最省力的路线. 做完 Lab 6 的连线不用拆,可以接下来做以下两个实验: Lab 203 网络时钟 核心代码如下: v ...

随机推荐

  1. ssh别名登录,非常适合从跳板机登录其他主机

    一般连主机会是这样的: ssh admin@IP 端口变了的话还要加上端口号 ssh admin@IP -p 10022 可以用ssh别名简化这个操作: vim .ssh/config 想要全局生效的 ...

  2. 【遗传编程/基因规划】Genetic Programming

    目录 背景介绍 程序表示 初始化 (Initialization) Depth定义 Grow方法 Full方法 Ramped half-and-half方法 适应度(Fitness)与选择(Selec ...

  3. 9.3 Go json

    9.3 Go json json是一种轻量级的数据交换格式,易于机器解析,是种key-value格式. JavaScript Object Notation是中种主流的数据格式. json常用于网络传 ...

  4. zabbix监控redis多实例cpu mem-自动发现

    1.自动发现实例端口脚本,用于zbx item prototypes #!/bin/bash REDIS_PORT=`ps aux |grep redis-server | grep -v 'grep ...

  5. asp.net MVC3.0 中@Html.Partial,@Html.Action,@Html.RenderPartial,@Html.RenderAction

    asp.net MVC3.0 中@Html.Partial,@Html.Action,@Html.RenderPartial,@Html.RenderAction 1.带有Render的方法返回值是v ...

  6. 解决2003 - 2003 - Can't connect to MySQL server on '127.0.0.1'(61 "Connection refused")

    1)右击数据库选择编辑连接2) 3)重新输入密码即可

  7. css3,transition,animation两种动画实现区别

    我们为页面设置动画时,往往会用到transition还有animation以及transfrom属性或者用到js. 其实通常情况下,对于使用js我们更加倾向于使用css来设置动画. transfrom ...

  8. Verilog语言中的系统任务和系统函数

    Verilog语言中预先定义了一些任务和函数,用于完成一些特殊的功能,它们被称为系统任务和系统函数,这些函数大多数都是只能在Testbench仿真中使用的,使我们更方便的进行验证. `timescal ...

  9. 【JAVA习题六】输入两个正整数m和n,求其最大公约数

    import java.util.Scanner; public class Oujilide欧几里得 { public static void main(String[] args) { // TO ...

  10. Python 每日一练(6)

    引言 今天的练习可以说是昨天的那个的反操作,今天要尝试将xls文件的内容写入xml文件中 所涉及到的库有xml,xlrd xls文件内容写入xml python操作excel主要用到xlrd和xlwt ...