Lab:Xv6 和 Unix 实用程序 本实验将使您熟悉xv6及其系统调用。
准备工作 获取实验室的xv 6源代码并切换到 util
分支:
1 2 3 4 5 6 7 $ git clone git://g.csail.mit.edu/xv6-labs-2020 Cloning into 'xv6-labs-2020' ... ... $ cd xv6-labs-2020 $ git checkout util Branch 'util' set up to track remote branch 'util' from 'origin' . Switched to a new branch 'util'
编译并运行XV6
如果成功即可进入sh
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 93cat 2 4 24256echo 2 5 23080 forktest 2 6 13272 grep 2 7 27560 init 2 8 23816kill 2 9 23024ln 2 10 22880ls 2 11 26448mkdir 2 12 23176rm 2 13 23160 sh 2 14 41976 stressfs 2 15 24016 usertests 2 16 148456 grind 2 17 38144wc 2 18 25344 zombie 2 19 22408 console 3 20 0
xv 6没有 ps
命令,但是,如果您键入 Ctrl-p ,内核将打印有关每个进程的信息。如果你现在尝试,你会看到两行: init
一个, sh
一个。
退出XV6:
您可以运行 make grade
以使用评分程序测试您的解决方案
sleep 为 xv6 实现 UNIX 程序 sleep;您的睡眠程序应在用户指定的 ticks 数量内暂停。tick 是 xv6 内核定义的时间概念,即定时器芯片两次中断之间的间隔时间。您的解决方案应放在 user/sleep.c 文件中。
在开始编码之前,请阅读xv 6手册的第1章。
查看 user/
中的一些其他程序(例如, user/echo.c
、 user/grep.c
和 user/rm.c
),了解如何获取传递给程序的命令行参数。
如果用户忘记传递参数,sleep应该打印一条错误消息。
命令行参数作为字符串传递;您可以使用 atoi
将其转换为整数(请参阅user/ulib. c)。
使用系统调用 sleep
。
参见 kernel/sysproc.c
,了解实现 sleep
系统调用的xv 6内核代码(查找 sys_sleep
);参见 user/user.h
,了解可从用户程序调用的 sleep
的C定义;参见 user/usys.S
,了解从用户代码跳转到 sleep
内核的汇编代码。
确保 main
调用 exit()
以退出程序。
将你的 sleep
程序添加到Makefile中的 UPROGS
;一旦你完成了, make qemu
将编译你的程序,你就可以从xv 6 shell运行它了。
1 2 3 4 5 6 $ make qemu ... init: starting sh $ sleep 10 (nothing happens for a little while ) $
注意这里的make grade等判分程序都是运行在linux的shell中而不是xv6的系统中
每个程序的开始都需要
1 #include "/kernel/tpyes.c"
添加程序到Makefile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include "kernel/types.h" #include "user/user.h" int main (int argc, char *argv[]) { if (argc < 2 ) { fprintf (2 , "Usage: sleep time\n" ); exit (1 ); } sleep (atoi (argv[1 ])); exit (0 ); }
请注意, make grade 运行所有测试,包括下面的分配。如果要运行一个作业的成绩测试,请键入:
1 2 3 ./grade-lab-util sleep 或者 make GRADEFLAGS=sleep grade
pingpong 编写一个程序,使用UNIX系统调用通过一对管道(每个方向一个管道)在两个进程之间“乒乓”一个字节。父代应该向子代发送一个字节;子进程应该打印“:received ping”,其中是它的进程ID,将管道上的字节写入父进程,然后退出;父节点应该从子节点读取字节,打印“:received pong”,然后退出。您的解决方案应该在文件 user/pingpong.c
中。
使用 pipe
创建管道。
使用 fork
创建子进程
使用 read
从管道读取,使用 write
写入管道。
使用 getpid
查找调用进程的进程ID。
将程序添加到Makefile中的 UPROGS
。
xv 6上的用户程序只有一组有限的库函数可供使用。您可以在 user/user.h
中查看列表;源(系统调用除外)在 user/ulib.c
、 user/printf.c
和 user/umalloc.c
中。
管道是一个小的内核缓冲区,作为一对文件描述符暴露给进程,一个用于读取一个用于写入。将数据写入管道的一端,就可以从管道的另一端读取数据。管道为进程提供了一种通信方式。
运行结果应该如下所示:
1 2 3 4 5 6 7 $ make qemu ... init: starting sh$ pingpong4 : received ping3 : received pong$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 #include "kernel/types.h" #include "user/user.h" int main (int argc,char * argv[]) { if (argc>1 ) { fprintf (2 ,"Only 1 argument is needed!\n" ); exit (1 ); } int p1[2 ]; int p2[2 ]; pipe (p1); pipe (p2); int pid; if ((pid=fork())<0 ) { fprintf (2 ,"fork error\n" ); exit (1 ); } else if (pid==0 ) { close (p1[1 ]); close (p2[0 ]); char buf[10 ]; read (p1[0 ],buf,4 ); close (p1[0 ]); printf ("%d: received %s\n" ,getpid (),buf); write (p2[1 ],"pong" ,strlen ("pong" )); close (p2[1 ]); } else { close (p1[0 ]); close (p2[1 ]); write (p1[1 ],"ping" ,strlen ("ping" )); close (p1[1 ]); char buf[10 ]; read (p2[0 ],buf,4 ); printf ("%d: received %s\n" ,getpid (),buf); close (p2[0 ]); } exit (0 ); }
find 编写一个简单的UNIX find程序:在目录树中查找具有特定名称的所有文件。您的解决方案应该在文件 user/find.c
中。
查看user/ls.c以了解如何读取目录。
使用递归允许查找下降到子目录。
不要递归到.和..
对文件系统的更改在qemu的运行过程中保持不变;要获得干净的文件系统,请运行 make clean ,然后运行 make qemu 。
你需要使用C字符串
注意==不像Python中那样比较字符串。使用strcmp()代替。
将程序添加到Makefile中的 UPROGS
。
1 2 3 4 5 6 7 8 9 10 $ make qemu ... init: starting sh $ echo > b $ mkdir a $ echo > a/b $ find . b ./b ./a/b $
fstat 系统调用可从文件描述符指向的 inode 中获取信息。它在 stat.h (kernel/stat.h)中定义为
1 2 3 4 5 6 7 8 9 10 11 #define T_DIR 1 #define T_FILE 2 #define T_DEVICE 3 struct stat { int dev; uint ino; short type; short nlink; uint64 size; };
xargs 编写一个简单版本的UNIX xargs程序:从标准输入中读取行,并为每行运行一个命令,将该行作为命令的参数提供。您的解决方案应该在文件 user/xargs.c
中。
1 2 3 $ echo hello too | xargs echo bye bye hello too $
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 #include "kernel/types.h" #include "user/user.h" #include "kernel/fs.h" #include "kernel/stat.h" #define FDSTDIN 0 #define FDSTDOUT 1 int main (int argc,char * argv[]) { char buf[10 ]; char * cur[MAXARG]; int i=0 ,j=0 ; for (i=0 ;i<argc-1 ;i++) { cur[i]=argv[i+1 ]; } cur[i]=malloc (512 ); while (read(FDSTDIN,buf,1 )==1 ) { if (i==argc-1 ) printf ("%s,%s\n" ,buf,cur[i]); switch (buf[0 ]) { case '\n' : { if (fork()==0 ) { exec(cur[0 ],cur); exit (0 ); } wait(0 ); for (int k=argc-1 ;k<=i;k++) { memset (cur[k],0 ,512 ); free (cur[k]); } i=argc-1 ; cur[i]=malloc (512 ); j=0 ; break ; } case ' ' : { i++; j=0 ; if (i<MAXARG) cur[i]=malloc (512 ); break ; } default : { cur[i][j++]=buf[0 ]; break ; } } } exit (0 ); }