Lab 1: Xv6 and Unix utilities
环境配置
- 机器OS:
ubuntu 20.04 - 安装实验相关包:
1
$ sudo apt-get install git build-essential gdb-multiarch qemu-system-misc gcc-riscv64-linux-gnu binutils-riscv64-linux-gnu
一、启动 xv6(难度:easy)
克隆
xv6代码并切换到util分支1
2
3
4
5
6
7$ git clone git://g.csail.mit.edu/xv6-labs-2021
Cloning into 'xv6-labs-2021'...
...
$ cd xv6-labs-2021
$ git checkout util
Branch 'util' set up to track remote branch 'util' from 'origin'.
Switched to a new branch 'util'构建并运行xv6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19$ make qemu
riscv64-unknown-elf-gcc -c -o kernel/entry.o kernel/entry.S
riscv64-unknown-elf-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -DSOL_UTIL -MD -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o kernel/start.o kernel/start.c
...
riscv64-unknown-elf-ld -z max-page-size=4096 -N -e main -Ttext 0 -o user/_zombie user/zombie.o user/ulib.o user/usys.o user/printf.o user/umalloc.o
riscv64-unknown-elf-objdump -S user/_zombie > user/zombie.asm
riscv64-unknown-elf-objdump -t user/_zombie | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$/d' > user/zombie.sym
mkfs/mkfs fs.img README user/xargstest.sh user/_cat user/_echo user/_forktest user/_grep user/_init user/_kill user/_ln user/_ls user/_mkdir user/_rm user/_sh user/_stressfs user/_usertests user/_grind user/_wc user/_zombie
nmeta 46 (boot, super, log blocks 30 inode blocks 13, bitmap blocks 1) blocks 954 total 1000
balloc: first 591 blocks have been allocated
balloc: write bitmap block at sector 45
qemu-system-riscv64 -machine virt -bios none -kernel kernel/kernel -m 128M -smp 3 -nographic -drive file=fs.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0
xv6 kernel is booting
hart 2 starting
hart 1 starting
init: starting sh
$使用
ls输出mkfs在初始文件系统中包含的文件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22$ ls
. 1 1 1024
.. 1 1 1024
README 2 2 2059
xargstest.sh 2 3 93
cat 2 4 24256
echo 2 5 23080
forktest 2 6 13272
grep 2 7 27560
init 2 8 23816
kill 2 9 23024
ln 2 10 22880
ls 2 11 26448
mkdir 2 12 23176
rm 2 13 23160
sh 2 14 41976
stressfs 2 15 24016
usertests 2 16 148456
grind 2 17 38144
wc 2 18 25344
zombie 2 19 22408
console 3 20 0使用
ctrl-a x退出qemu,Ctrl-a按完松手再按x退出
二、Sleep实现(难度:easy)
实现xv6的UNIX程序sleep:您的sleep应该暂停到用户指定的计时数。一个滴答(tick)是由xv6内核定义的时间概念,即来自定时器芯片的两个中断之间的时间
学习main 函数的两个参数argc 和argv[] 的含义,将参数传入sleep 系统调用即可实现相关功能。
argc是命令行总的参数个数。argv[]为保存命令行参数的字符串指针。其中第0个参数是程序的全名,以后的参数为命令行后面跟的用户输入的参数。
1 | // "user/sleep.cpp" |
在Makefile 中UPROGS 处添加$U/_sleep\
三、pingpong实现(难度:easy)
实现xv6的UNIX程序pingpong:编写一个程序,使用UNIX系统调用在一对管道上的两个进程之间 “ping-pong” 一个字节,每个方向一个。父级应该向子级发送一个字节; 子级应该打印 “<pid>: received ping”,其中 <pid> 是其进程ID,将管道上的字节写入父级,然后退出; 父级应该从子级读取字节,打印 “<pid>: received pong”,然后退出。
学习管道的使用,通过使用pipe 系统调用来实现管道功能,通过fork 系统调用来实现子进程的创建。
1 | // "user/pingpong.cpp" |
在Makefile 中UPROGS 处添加$U/_pingpong\
四、Primes实现(难度:moderate/hard)
使用管道编写prime sieve(筛选素数)的并发版本。
- 由于一个数的因数一定比该数本身小,所以可以使用比当前数小的所有素数进行筛选
- 如果当前数不是已确定的所有素数的倍数,则该数为素数
- 若该数为素数,则以该数为基创建子进程,并和与该数最接近的素数建立管道
- 对每个数都使用串联的所有素数进行判断,直到确定其为合数或素数
1 | // "user/primes.cpp" |
在Makefile 中UPROGS 处添加$U/_primes\
五、find实现(难度:Moderate)
写一个简化版本的UNIX的find程序:查找目录树中具有特定名称的所有文件
学习ls 的实现逻辑,遍历目录树,寻找指定的文件名即可。
- 注意:处理目录树中的两个特殊文件
"."和".."
1 | // "user/find.cpp" |
在Makefile 中UPROGS 处添加$U/_find\
六、xargs实现(难度:Moderate)
编写一个简化版UNIX的xargs程序:它从标准输入中按行读取,并且为每一行执行一个命令,将行作为参数提供给命令。
这个题目在第一次做的时候还是很难理解的,主要是参考了这篇文章
1、xargs的作用
我们知道管道(pipe) 是将前一个进程的输出(stdout) 作为下一下进程的输入(stdin) ,但是管道命令仅能处理standard output ,也就是说类似less 、head 、tail 等可以接受标准输入的命令。
而有很多命令不接收管道的传递方式,比如ls 和echo ,它们不能直接接收标准输入,而需要接收命令行参数,这时候就需要使用xargs 命令将管道传输过来的stdin 进行处理然后传递到命令的参数位上。也就是说,xargs 在这里执行了两个行为:
- 处理管道左侧的标准输入
- 处理后传递到正确的位置上
例如 echo "one two three" | xargs mkdir 等价于执行 mkdir one two three ,如果不加 xargs 则会提示 mkdir 缺少操作参数。
2、实现思路
事实上,就是从标准输入中读取字符串,直到遇到 \n 或 \r 则完成一次输入, 然后将所有的参数都读进来放在一个字符串数组里,然后将每一行都作为一个参数 fork 出一个子进程来执行 exec 系统调用。
1 | // user/xargs.cpp |
在Makefile 中UPROGS 处添加$U/_xargs\
实验结果
