MIT 6.S081 Operating System Enginerring Lab1 Utilities
这里给出Lab1 Utilities的解析,由于对很多linux函数不太熟悉,所以还是花了比较多的时间。
学习资料:
- https://pdos.csail.mit.edu/6.828/2020/schedule.html
- https://mit-public-courses-cn-translatio.gitbook.io/mit6-s081/
- https://th0ar.gitbooks.io/xv6-chinese/content/
- https://www.bilibili.com/video/BV19k4y1C7kA
参考资料:
Boot xv6 (easy)
这部分略过,需要注意的一点是,评测脚本grade-lab-util是python程序,这里对第一行进行修改,将
#!/usr/bin/env python
修改为:
#!/usr/bin python
测试命令由
./grade-lab-util sleep
修改为:
sudo python3 grade-lab-util sleep
另一个需要注意的点是在实现对应功能后,需要更新Makefile的UPROGS部分,例如实现了sleep.c后,要在UPROGS处追加:
$U/_sleep\
sleep (easy)
比较基础,调用内部的sleep函数即可。
代码:
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc, char *argv[]){
int n;
if (argc != 2){
fprintf(2, "Please enter a number!\n");
exit(1);
}else{
n = atoi(argv[1]);
sleep(n);
exit(0);
}
}
测试:
sudo python3 grade-lab-util sleep
make: 'kernel/kernel' is up to date.
== Test sleep, no arguments == sleep, no arguments: OK (1.1s)
== Test sleep, returns == sleep, returns: OK (0.8s)
== Test sleep, makes syscall == sleep, makes syscall: OK (1.0s)
pingpong (easy)
此题考查了pipe和fork的理解和使用,可以参考课本1.3节的例子。
pipe的输入为长度为2的int数组p,其中p[0]对应输入文件描述符,p[1]对应输出文件描述符。
该程序的流程如下:
父进程:父进程向子进程传递信息,等待子进程结束,读取子进程传递的信息。
子进程:读取父进程传递的信息,传递给父进程信息。
代码:
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc, char *argv[]){
int p[2];
char buf[2];
char *parmsg = "a";
char *chimsg = "b";
pipe(p);
if (fork() == 0){
if (read(p[0], buf, 1) != 1){
fprintf(2, "Can't read from parent!\n");
exit(1);
}
printf("child receive: %c\n", buf[0]);
close(p[0]);
printf("%d: received ping\n", getpid());
if (write(p[1], chimsg, 1) != 1){
fprintf(2, "Can't write to parent!");
}
close(p[1]);
exit(0);
}else{
if (write(p[1], parmsg, 1) != 1){
fprintf(2, "Can't write to child!\n");
exit(1);
}
close(p[1]);
wait(0);
if (read(p[0], buf, 1) != 1){
fprintf(2, "Can't read from child!");
exit(1);
}
printf("parent receive: %c\n", buf[0]);
close(p[0]);
printf("%d: received pong\n", getpid());
exit(0);
}
}
效果:
$ pingpong
child receive: a
4: received ping
parent receive: b
3: received pong
测试:
sudo python3 grade-lab-util pingpong
make: 'kernel/kernel' is up to date.
== Test pingpong == pingpong: OK (1.4s)
primes (moderate)/(hard)
此题需要仔细理解下图:
代码:
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
void primes(int *fd){
int p, d;
// 关闭写
close(fd[1]);
if(read(fd[0], (void *)&p, sizeof(p)) != sizeof(p)){
fprintf(2, "Read fail!\n");
exit(1);
}
printf("prime %d\n", p);
if (read(fd[0], (void *)&d, sizeof(d))){
int fd1[2];
pipe(fd1);
if (fork() == 0){
primes(fd1);
}else{
// 关闭读
close(fd1[0]);
do{
if (d % p != 0){
write(fd1[1], (void *)&d, sizeof(d));
}
}while(read(fd[0], (void *)&d, sizeof(d)));
// 关闭读
close(fd[0]);
// 关闭写
close(fd1[1]);
wait(0);
}
}
exit(0);
}
int main(int argc, char *argv[]){
int fd[2];
int start = 2;
int end = 35;
pipe(fd);
if (fork() == 0){
primes(fd);
}else{
close(fd[0]);
for (int i = start; i <= end; i++){
if (write(fd[1], (void *)&i, sizeof(i)) != 4){
fprintf(2, "Write fail!\n");
exit(1);
}
}
close(fd[1]);
wait(0);
}
exit(0);
}
测试:
sudo python3 grade-lab-util primes
make: 'kernel/kernel' is up to date.
== Test primes == primes: OK (1.4s)
(Old xv6.out.primes failure log removed)
find (moderate)
需要使用ls.c中的代码,整体思路如下:
- 判断当且路径是否为文件,如果是文件则直接判断文件名是否和目标文件名相同。
- 否则当且路径为目录,遍历目录下文件,递归处理。
代码:
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
char*
fmtname(char *path)
{
static char buf[DIRSIZ+1];
char *p;
// Find first character after last slash.
for(p=path+strlen(path); p >= path && *p != '/'; p--)
;
p++;
// Return blank-padded name.
if(strlen(p) >= DIRSIZ)
return p;
memmove(buf, p, strlen(p));
memset(buf+strlen(p), ' ', DIRSIZ-strlen(p));
return buf;
}
void find(char *path, char *filename){
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;
}
if (read(fd, &de, sizeof(de)) != sizeof(de)){
exit(1);
}
switch(st.type){
case T_FILE:
// 当前路径为文件, 直接判断
if (strcmp(de.name, filename) == 0){
printf("%s/%s\n", path, filename);
}
break;
case T_DIR:
// 当前路径为文件夹, 遍历
if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){
fprintf(2, "ls: path too long\n");
break;
}
// buf下一层路径
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, DIRSIZ);
p[DIRSIZ] = 0;
// 获得文件描述
if(stat(buf, &st) < 0){
fprintf(2, "find: cannot stat %s\n", buf);
continue;
}
if (st.type == T_FILE){
if (strcmp(de.name, filename) == 0){
printf("%s\n", buf);
}
}else if (st.type == T_DIR){
// 如果当前目录是文件夹, 则递归处理, buf为下一级目录
find(buf, filename);
}
}
break;
}
close(fd);
}
int main(int argc, char *argv[]){
if (argc != 3){
fprintf(2, "Please enter a dir and a filename!\n");
exit(1);
}else{
char *path = argv[1];
char *filename = argv[2];
find(path, filename);
exit(0);
}
}
效果:
$ echo > b
$ mkdir a
$ echo > a/b
$ find . b
./b
./a/b
测试:
sudo python3 grade-lab-util find
make: 'kernel/kernel' is up to date.
== Test find, in current directory == find, in current directory: OK (0.9s)
== Test find, recursive == find, recursive: OK (1.0s)
xargs (moderate)
xargs介绍:
https://wangchujiang.com/linux-command/c/xargs.html
整体思路:
- 将xargs命令传入的参数保存至数组,形式为cmd, arg[0], … , arg[k - 1];
- 解析输入参数,根据\n将参数划分至多行;
- 对每行参数根据空格划分,更新参数数组为cmd, arg[0], … , arg[k - 1], arg[k], … , arg[k + l - 1];
- 利用exec调用函数;
代码:
#include "kernel/types.h"
#include "kernel/stat.h"
#include "kernel/param.h"
#include "user/user.h"
void copy(char **p1, char *p2){
*p1 = malloc(strlen(p2) + 1);
strcpy(*p1, p2);
}
// i为起始下标
int readLine(char **pars, int i){
int d = 1024;
char buf[d];
int j = 0;
// 读取1行
while (read(0, buf + j, 1)){
// 按行划分
if (buf[j] == '\n'){
buf[j] = 0;
break;
}
j++;
if (j == d){
fprintf(2, "Parameters are too long!\n");
exit(1);
}
}
// 没有读取内容
if (j == 0){
return -1;
}
// 按照空格划分
int k = 0;
while (k < j){
if (i > MAXARG){
fprintf(2, "Too much parameters!\n");
exit(1);
}
// ' abc sdf'
// 忽略
while ((k < j) && (buf[k] == ' ')){
k++;
}
// 起始位置
int l = k;
// 保留字符
while ((k < j) && (buf[k] != ' ')){
k++;
}
// 注意需要k++
buf[k++] = 0;
copy(&pars[i], buf + l);
i++;
}
return i;
}
int main(int argc, char *argv[]){
if (argc < 2){
printf("Please enter more parameters!\n");
exit(1);
}else{
int i;
char *pars[MAXARG];
for (i = 1; i < argc; i++){
copy(&pars[i - 1], argv[i]);
}
int end;
while ((end = readLine(pars, argc - 1)) != -1){
pars[end] = 0;
if (fork() == 0){
exec(pars[0], pars);
exit(1);
}else{
wait(0);
}
}
exit(0);
}
}
效果:
$ sh < xargstest.sh
$ $ $ $ $ $ hello
hello
hello
$ $
测试:
sudo python3 grade-lab-util xargs
make: 'kernel/kernel' is up to date.
== Test xargs == xargs: OK (1.4s)
其中user/xargstest.sh中内容为:
mkdir a
echo hello > a/b
mkdir c
echo hello > c/b
echo hello > b
find . b | xargs grep hello
编写脚本后需要在Makefile中UEXTRA更新:
UEXTRA=
ifeq ($(LAB),util)
UEXTRA += user/xargstest.sh
UEXTRA += user/findregtest.sh
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Doraemonzzz!
评论
ValineLivere