MIT6.S081-Lab Utilities
作者:互联网
开始日期:22.2.24
操作系统:Ubuntu20.0.4
Link:Lab Utilities
目录Lab Utilities
环境配置
每次的环境配置都是一段折磨又快乐的时光,这个实验的环境配置主要参考搭建MIT6.S081的实验环境
没有很复杂,再参考一下官网对Ubuntu的描述即可。
实验内容
在实验之前
- 可以参考实验内容翻译来阅读
- 需要阅读参考书的Chapter 1
- 该课程在学完CS:APP3e之后来,会比较流畅,许多前置知识已经了解了
- 可能需要事先了解的UINX(系统)函数
- fork()
- wait()
- xargs()
lab Boot xv6 (easy)
-
参照着启动xv6就可以了,实验环境搭建完之后没啥难度
-
注意clone下来的
xv6-labs-2021
文件夹存放在用户文件夹当中,一般就在桌面的左上角 -
To quit qemu type: Ctrl-a x.
- 关闭qemu的方式是:先同时按ctrl+a,然后按x
sleep (easy)
-
功能:使shell睡眠
-
对照hints来写即可,但可能一开始不熟悉这种方式,写起来有点迷惑是很正常的,慢慢搞,搞得下来的
-
本题很简单,调用系统函数
sleep()
即可,但要注意:- 传入的指针数组
*argv[]
存储的是char
类型,需要用atoi()
把char
转换为int
- 传入的指针数组
-
参考代码:
#include"kernel/types.h" #include"kernel/stat.h" #include"user/user.h" int main(int agrc, char* agrv[]){ /* argc is the number of command, argv[] store the context of parameters */ if(agrc == 1){ printf("Please enter parameter of int"); } else { int time = atoi(agrv[1]); sleep(time); } exit(0); }
pingpong (easy)
-
功能:父子程序通过管道传递信息
-
这里开始涉及到pipe(管道)的知识了,笔者一开始也是比较迷糊,但实际上理解之后就容易了
-
调用
pipe()
函数即可建立管道,但注意要提前定义一个存放两个int元素的数组
用来存放fd(file descriptor, 文件描述符) -
这个管道是以半双工的方式进行交流的,以防止出意外,也就是说,在使用管道读之前,得关闭写端;在使用管道写之前,得关闭读端。
The fact that read blocks until it is impossible for new data to arrive is one reason that it’s important for the child to close the write end of the pipe before executing wc above: if one of wc ’s file descriptors referred to the write end of the pipe, wc would never see end-of-file.
BOOK-RISCV-REV2 Chapter 1-
这里建立了两个通道,一个是child的,一个是parent的
- 对于child,读写之前需要关闭父管道的写端,子管道的读端
- 对于parent,读写之前需要关闭父管道的读端,子管道的写端
-
灵魂图示
-
-
-
参考代码
/* Half-duplex Communication */ #include"kernel/types.h" #include"kernel/stat.h" #include"user/user.h" int main(int argc, char* argv[]){ int child_pfd[2]; int parent_pfd[2]; char buf[4]; /* "ping"or"pong" have 4 char */ pipe(child_pfd); pipe(parent_pfd); /* child */ if(fork() == 0){ int child_pid = getpid(); /* close write of parent and read of child */ close(parent_pfd[1]); close(child_pfd[0]); /*2: received "ping" then child print */ read(parent_pfd[0], buf, sizeof(buf)); printf("%d: received %s\n", child_pid, buf); /*3: write 4 bytes to parent */ write(child_pfd[1], "pong", sizeof(buf)); exit(0); } /* parent */ else{ int parent_pid = getpid(); /* close read of parent and write of child */ close(parent_pfd[0]); close(child_pfd[1]); /*1: sent 4 bytes to child */ write(parent_pfd[1], "ping", sizeof(buf)); /*4: received "pong" then parent print */ read(child_pfd[0], buf, sizeof(buf)); printf("%d: received %s\n", parent_pid, buf); wait(0); exit(0); } }
primes (moderate)/(hard)
-
给一个
2 ~ 35
的数组,输出其中的所有素数(限制:必须使用通道) -
这道题使用迭代,难度比较大
- 在子程序,孙程序,曾孙程序...中迭代,不断地filter(过滤),每次过滤的结果可能是素数,也可能不是,但到最后一次的结果一定是素数。
- 每次的
numbers
(结果)会传递给子程序用来继续过滤,但每次我们只打印一个prime
- 注意每次完成读操作之后,要关闭读端
-
参考代码
#include"kernel/types.h" #include"kernel/stat.h" #include"user/user.h" #define LIMIT 35 #define INT_SIZE 4 #define READ 0 #define WRITE 1 #define NUMBERLIMIT 34 /* real limit of number is (35 - 2 + 1) = 34 */ #define UNPRIME 0 /* 0 is not prime */ void print_and_filter_primes(int* fd); int main(){ int first_fd[2]; int numbers[NUMBERLIMIT]; /* build first_fd*/ pipe(first_fd); /* first child */ if (fork() == 0) print_and_filter_primes(first_fd); /* first parent */ else{ close(first_fd[READ]); /* feed numbers */ for (int i = 2; i <= LIMIT; ++i) numbers[i - 2] = i; write(first_fd[WRITE], &numbers, (NUMBERLIMIT) * INT_SIZE); close(first_fd[WRITE]); wait(0); exit(0); } return 0; } void print_and_filter_primes(int* fd){ close(fd[WRITE]); int prime = UNPRIME; int numbers[LIMIT]; int next_fd[2]; int temp; int count = 0; read(fd[READ], (int *)&prime, INT_SIZE); if (prime == UNPRIME) exit(0); /* the first number => 2 => is_prime */ /* each time only print a prime */ printf("prime %d\n", prime); /* filter numbers as next input */ /* the next numbers may prime or un_prime */ /* but the last number is prime */ while (read(fd[READ], &temp, INT_SIZE) > 0) if (temp % prime != 0) numbers[count++] = temp; /* aviod fd overflow*/ close(fd[READ]); /* build next_fd*/ pipe(next_fd); /* next child */ if (fork() == 0){ print_and_filter_primes(fd); } /* next parent */ else{ close(next_fd[READ]); write(next_fd[WRITE], &numbers, count * INT_SIZE); close(next_fd[WRITE]); wait(0); exit(0); } }
find (moderate)
-
功能:从根目录(
.
)中找出所有目标文件(name
) -
这道题的难度主要在于对
ls.c
的理解,要比较透彻才行 -
修改的基本都在switch
-
对于
file
,我们需要调用函数compare()
后将结果路径打印出来- 之所以要额外写个函数,是因为要一次性满足递归(recursion)下降,以便最后时一次性递归上升(可以理解为:在stack中一次性全部pop,之后一次性全部push,而不是一下pop,一下push)
compare()
还需要辅助函数get_path_lastname()
找出最后的名字来对比,该函数的主要思路是从最后一个char开始找到第一个slash('/')- eg
./a/c/b
中,b
是最后一个char,从左数第三个/
是对于b的第一个slash('/')
- eg
-
对于
directory
,我们需要递归find
继续进入,同时解决两个问题- 第一,组装新的路径,主要用到了追加函数
memmove()
和指针p
,注意:路径本身是字符串,c风格的字符串最后一个字符是'\0'
;标准库函数strlen(s)
可以返回字符串参数s
的长度,但长度不包括末尾的'\0'
。 - 第二,防止死循环,不进入当前目录
.
和父目录..
;节约时间,不进入空文件de.inum == 0
- 第一,组装新的路径,主要用到了追加函数
-
-
参考代码
#include "kernel/types.h" #include "kernel/stat.h" #include "user/user.h" #include "kernel/fs.h" void find(char *path, char *name); char* get_path_lastname(char *path); void compare(char *path_lastname, char *name); int main(int argc, char *argv[]) { /* // Input Error! if (argc < 3){ printf("Input Error!"); exit(0); } */ find(argv[1], argv[2]); exit(0); } void find(char *path, char *name) { char buf[512], *p; int fd; struct dirent de; struct stat st; if ((fd = open(path, 0)) < 0) { fprintf(2, "find: cannot open %s\n", path); return; } if (fstat(fd, &st) < 0) { fprintf(2, "find: cannot stat %s\n", path); close(fd); return; } switch (st.type){ case T_FILE: /* empty dir is file(T_FILE) */ /* must use recursion in order to push and pop*/ compare(path, name); break; case T_DIR: if (strlen(path) + 1 + DIRSIZ + 1 > sizeof(buf)){ printf("ls: 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) == 0 || strcmp("..", de.name) == 0) continue; memmove(p, de.name, strlen(de.name)); p[strlen(de.name)] = '\0'; find(buf, name); } break; } close(fd); } char* get_path_lastname(char *path) { static char buf[DIRSIZ+1]; char *p; /* order: from the last char to first slash('/') */ for(p = path + strlen(path); p >= path && *p != '/'; p--) ; /* since p-- point to last slash('/') */ /* p++ point to last char */ p++; // Return file name to cmp if(strlen(p) >= DIRSIZ) return p; memmove(buf, p, sizeof(p)); p[strlen(buf)] = '\0'; return buf; } void compare(char *path, char *name){ if(strcmp(get_path_lastname(path), name) == 0) printf("%s\n", path); return; } /* $ make qemu ... init: starting sh $ echo > b // build a dir called 'b' that 'b' is sub-dir of '.' $ mkdir a // build a dir called 'a' that 'a' is sub-dir of '.' $ echo > a/b // build a dir called 'b' that 'b' is sub-dir of 'a' $ find . b // find file that name is 'b' from system dir('.') ./a/b $ */ /* hierarchy of dir // Directory is a file containing a sequence of dirent structures. #define DIRSIZ 14 struct dirent { ushort inum; // the numbers of dir containing char name[DIRSIZ]; }; */ /* status of dir or file or device #define T_DIR 1 // Directory #define T_FILE 2 // File #define T_DEVICE 3 // Device struct stat { int dev; // File system's disk device uint ino; // Inode number short type; // Type of file short nlink; // Number of links to file uint64 size; // Size of file in bytes }; */
xargs (moderate)
-
功能:追加将要调用函数的参数,当遇到
\n
时就立刻执行-
参考链接:xargs()
-
eg1.
xargs
为将要调用的函数echo
追加了参数hello too
xargs echo bye hello too => bye hello too
-
eg2. 先调用
echo
,它的输出结果hello too
作为xargs
的输入,成为将要调用的函数echo
的追加参数echo hello too | xargs echo bye => bye hello too
-
-
all_args_buffer
存储了所有参数 -
我们传递给exec运行的是指针数组的地址
args_parray
,里面存储了将要执行的参数 -
当遇到
\n
执行完之后,要回归原来的位置-
eg. 当遇到
\n
执行完之后,要回归原来的位置(echo
)echo "1\n2" | xargs -n 1 echo line => line 1 line 2
-
-
参考代码
#include"kernel/types.h" #include"kernel/stat.h" #include"user/user.h" #include "kernel/fs.h" #include "kernel/param.h" int main(int argc, char **argv) { int i, j; char *args_parray[MAXARG]; char all_args_buffer[256]; char *ptr_buffer = all_args_buffer; char *ptr_array = ptr_buffer; for(i = 1, j = 0; i < argc; i++) args_parray[j++] = argv[i]; /* when loop break, j == argv - 1 */ int length; int total_length = 0; while (read(0, ptr_buffer, MAXARG) > 0){ /* 0 is pipe of reading */ total_length += length; if(total_length > 256){ printf("the args is too long!"); exit(0); } for(i = 0; i < length; ++i){ if(ptr_buffer[i] == ' '){ ptr_buffer[i] = '\0'; /* c-style: the last of string has '\0' */ args_parray[j++] = ptr_array; /* add new args to args_parray */ ptr_array = &ptr_buffer[i + 1]; /* ptr_array point to next */ } else if(ptr_buffer[i] == '\n'){ ptr_buffer[i] = '\0'; args_parray[j++] = ptr_array; args_parray[j] = '\0'; /* as args of exec (c-style: string has '\0') */ ptr_array = &ptr_buffer[i + 1]; if(fork() == 0){ exec(argv[1], args_parray); exit(0); } j = argc - 1; /* comeback, eg: echo "1\n2" | xargs -n 1 echo line */ wait(0); } } ptr_buffer + length; } exit(0); }
-
根据提示,我们还需要修改
find()
,以解决从.sh
文件中读出的每条命令都加上$
的问题。我们希望只有一个$
,笔者目前没有解决这个bug
。
总结
-
完成日期22.3.8
-
刚开学期间有许多杂事,返校需要自费核酸之类的,还有免修。劳累之后就得好好休息,就落下了
-
难度目前没有很大
-
最近在听《想和你漫步在午后》夏小调,《可风》黄建为
-
自测试图(笔者未解决关于
time.txt
的bug
)
标签:MIT6,buf,Utilities,char,int,fd,path,include,S081 来源: https://www.cnblogs.com/duile/p/15981486.html