开学!
启动 xv6 git 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 git clone git://g.csail.mit.edu/xv6-labs-2022 git status git log git checkout util git commit -am 'my solution for util lab exercise 1 # 查看相比上一次 commit 的变化 git diff # 查看相比最初的变化 git diff origin/util
建立并运行 xv6
里面有一些很基本的命令
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 xv6 kernel is booting hart 1 starting hart 2 starting init: starting sh $ ls . 1 1 1024 .. 1 1 1024 README 2 2 2227 xargstest.sh 2 3 93 cat 2 4 32832echo 2 5 31728forktest 2 6 15680 grep 2 7 36176 init 2 8 32152 kill 2 9 31712ln 2 10 31520ls 2 11 34728mkdir 2 12 31784rm 2 13 31768sh 2 14 53960 stressfs 2 15 32496 usertests 2 16 181776 grind 2 17 47696 wc 2 18 33832zombie 2 19 31168 console 3 20 0
-甚至都没有 clear
Ctrl-p 打印进程信息
Ctrl-a x 退出 qemu
结论:做任何事之前先看介绍
成绩测试 1 2 3 4 5 6 7 make grade ./grade-lab-util name make GRADEFLAGS=name grade
sleep 在 bash 中测试,能够多参数且如果一个参数错误就不执行
user/sleep.c 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 #include "kernel/types.h" #include "user/user.h" int isDigitStr (char *str) { for (int i = 0 ; i < strlen (str); ++i) { if (str[i] < '0' || str[i] > '9' ) { return 0 ; } } return 1 ; } int main (int argc, char *argv[]) { int status = 0 ; if (argc == 1 ) { printf ("sleep: missing operand!\n" ); status = -1 ; } for (int i = 1 ; i < argc; ++i) { if (!isDigitStr(argv[i])) { printf ("sleep: invalid time interval \'%s\'\n" , argv[i]); status = -1 ; } } for (int i = 1 ; i < argc && !status; ++i) { sleep(atoi(argv[i])); } exit (status); }
源代码放在 user
目录下,每次写完一个程序在 Makefile 中的 UPROGS 下添加一行 $U/_sleep\
然后 make qemu
编译运行
之后可以在 qemu 外运行 /grade-lab-util sleep
进行单项测试
1 2 3 4 5 $ ./grade-lab-util sleep make: 'kernel/kernel' is up to date . == Test sleep , no arguments == sleep , no arguments: OK (1.5s) == Test sleep , returns == sleep , returns: OK (0.6s) == Test sleep , makes syscall == sleep , makes syscall: OK (1.0s)
pingpong 简单题
父进程发送子进程一个字节,子进程收到后再给父进程一个字节
user/pingpong.c 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 #include "kernel/types.h" #include "user/user.h" int main (int argc, char *argv[]) { int pid, p[2 ]; pipe(p); pid = fork(); if (pid == 0 ) { if (read(p[0 ], 0 , 1 )) { pid = getpid(); printf ("%d: received ping\n" , pid); write(p[1 ], "H" , 1 ); exit (0 ); } } else { write(p[1 ], "H" , 1 ); if (read(p[0 ], 0 , 1 )) { pid = getpid(); printf ("%d: received pong\n" , pid); exit (0 ); } } exit (-1 ); }
primes 有点难度
想了好久,感觉是要用递归,但是没想出来怎么写
想到在看网课的时候,进入子进程先把 close(0)
,然后 dup(p[1])
,也就是把子进程的标准输入改为管道的输入了,这样就容易写递归了
当从输入接收不到 prime 的时候 exit(0)
这里注意 dup(p[1])
后要把管道都给关了
user/primes.c 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 #include "kernel/types.h" #include "user/user.h" void printPrime (int prime) { printf ("prime %d\n" , prime); } void primes () { int prime, n, pid, p[2 ]; if (!read(0 , &prime, sizeof (prime))) { exit (0 ); } printf ("prime %d\n" , prime); pipe(p); pid = fork(); if (pid == 0 ) { close(0 ); dup(p[0 ]); close(p[0 ]); close(p[1 ]); primes(); exit (0 ); } else { while (read(0 , &n, sizeof (n))) { write(p[1 ], &n, 4 ); } close(p[1 ]); int status; wait(&status); exit (0 ); } exit (-1 ); } int main (int argc, char *argv[]) { int pid, p[2 ]; pipe(p); pid = fork(); if (pid == 0 ) { close(0 ); dup(p[0 ]); close(p[0 ]); close(p[1 ]); primes(); exit (0 ); } else { for (int i = 2 ; i < 36 ; ++i) { write(p[1 ], &i, 4 ); } close(p[1 ]); int status; wait(&status); exit (0 ); } exit (-1 ); }
find 同样也是递归,从目录里查找文件可以参考 ./user/ls.c
当找的是文件或者时比较名字
当找的是目录时,从 fd 读取 struct dirent[]
,表示目录下的每个文件,里面有 name,表示文件名,注意过滤 .
和 ..
user/find.c 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 #include "kernel/types.h" #include "kernel/fcntl.h" #include "kernel/stat.h" #include "kernel/fs.h" #include "user/user.h" void find (char *path, char *filename) { char buf[512 ]; char *name, *p; int fd; struct stat st ; struct dirent de ; if ((fd = open(path, O_RDONLY)) < 0 ) { printf ("find: cannot open %s\n" , path); return ; } if (fstat(fd, &st) < 0 ) { printf ("find: cannot stat %s\n" , path); return ; } switch (st.type) { case T_DEVICE: case T_FILE: name = path; for (int i = strlen (path)-1 ; i >= 0 ; --i) { if (path[i] == '/' ) { name = &path[i+1 ]; break ; } } if (!strcmp (name, filename)) { printf ("%s\n" , path); } break ; case T_DIR: if (strlen (path) + 1 + DIRSIZ + 1 > sizeof (buf)) { printf ("find: path too long\n" ); break ; } strcpy (buf, path); p = buf + strlen (buf); *p++ = '/' ; while (read(fd, &de, sizeof (de)) == sizeof (de)) { if (de.inum == 0 || !strcmp (de.name, "." ) || !strcmp (de.name, ".." )) { continue ; } memmove(p, de.name, DIRSIZ); p[DIRSIZ] = 0 ; find(buf, filename); } break ; } close(fd); } int main (int argc, char *argv[]) { if (argc != 3 ) { printf ("find: invalid argument\n" ); exit (-1 ); } find(argv[1 ], argv[2 ]); exit (0 ); }
xargs 一开始没懂 sh
怎么实现管道
测试发现就是将管道的读端作为 |
右边程序的标准输入
主要是判断什么时候跳出循环
user/xargs.c 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 #include "kernel/types.h" #include "kernel/param.h" #include "user/user.h" int main (int argc, char *argv[]) { int idx = 0 , pid; int status; char buf; char *new_argv[argc+1 ]; for (int i = 1 ; i < argc; ++i) { new_argv[i-1 ] = argv[i]; } while (read(0 , &buf, 1 )) { idx = 0 ; new_argv[argc-1 ] = (char *)malloc (MAXARG); do { if (buf == '\n' ) { new_argv[argc-1 ][idx] = '\0' ; new_argv[argc] = 0 ; break ; } new_argv[argc-1 ][idx++] = buf; } while (read(0 , &buf, 1 )); pid = fork(); if (pid == 0 ) { exec(new_argv[0 ], new_argv); printf ("wrong command\n" ); exit (-1 ); } else { wait(&status); } } exit (0 ); }
Optional challenge exercises 写一个 uptime 程序来调用 uptime 系统调用 直接调用 uptime 然后打印返回值就好了
对 grep 实现正则匹配 yysy,对正则表达式不是很了解
改造 sh #todo