这个实验主要学习了常用的一些系统调用。
Lab 1: Unix utilities
Boot xv6 (easy)
git克隆,切换分支,qemu。根据要求进行操作即可。
$ git clone git://g.csail.mit.edu/xv6-labs-2020
$ cd xv6-labs-2020
$ git checkout util
$ make qemu
sleep (easy)
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"int main(int argc ,char *argv[])
{if(argc < 2 ){fprintf(2,"please enter a number!");exit(1);}else{int n = atoi(argv[1]);sleep(n);exit(0);}}
在 Makefile 中将 sleep 加入构建目标里。
UPROGS=\$U/_cat\$U/_echo\$U/_forktest\$U/_grep\$U/_init\$U/_kill\$U/_ln\$U/_ls\$U/_mkdir\$U/_rm\$U/_sh\$U/_stressfs\$U/_usertests\$U/_grind\$U/_wc\$U/_zombie\$U/_sleep\ . # here !!!
pingpong (easy)
管道题,使用 fork() 复制本进程创建子进程,然后使用管道进行相互通信。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"int main(int argc ,char *argv[])
{int pp2c[2],pp2p[2];pipe(pp2c);pipe(pp2p);int n = fork();if(n != 0 ){write(pp2c[1],"a",1);char buff;read(pp2p[0],&buff,1);printf("%d: received pong\n",n);}else{char buff;read(pp2c[0],&buff,1);printf("%d: received ping\n",n);write(pp2p[1],&buff,1);}exit(0);}
primes (moderate) / (hard)
观察下图进行进行理解,使用fork实现素数筛的功能。
// primes.c
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"void sieve(int pleft[2]){int p ;read(pleft[0] ,&p,sizeof(p));if(p == -1){exit(0);}printf("prime %d\n", p);int pright[2];pipe(pright);if(fork() == 0){// 子进程close(pright[1]);close(pleft[0]);sieve(pright);}else{close(pright[0]);int buff ;while(read(pleft[0] ,&buff,sizeof(buff)) && buff != -1){if(buff %p != 0 ){write(pright[1] ,&buff,sizeof(buff));}}buff = -1;write(pright[1] ,&buff,sizeof(buff));wait(0);exit(0);}
}int main(int argc,char* argv[]){int inputpipe[2];pipe(inputpipe);if(fork() == 0){// 子进程close(inputpipe[1]);sieve(inputpipe);exit(0);}else{close(inputpipe[0]);int i ;for( i = 2 ; i <=35;i++){write(inputpipe[1] ,&i,sizeof(i) );}i =-1;write(inputpipe[1] ,&i,sizeof(i) );}wait(0);exit(0);
}
find (moderate)
根据ls.c 改造得到
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"void find(char *path,char *target)
{char buf[512], *p;int fd;struct dirent de;struct stat st;if((fd = open(path, 0)) < 0){fprintf(2, "ls: cannot open %s\n", path);return;}if(fstat(fd, &st) < 0){fprintf(2, "ls: cannot stat %s\n", path);close(fd);return;}switch(st.type){case T_FILE:if(strcmp(path+strlen(path) - strlen(target) ,target) == 0)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, ".")==0 || strcmp(de.name, ".." )==0 )continue;memmove(p, de.name, DIRSIZ);//每次循环都会被覆盖p[DIRSIZ] = 0;if(stat(buf, &st) < 0){printf("find: cannot stat %s\n", buf);continue;}// printf(buf);// printf("\n");find(buf, target); // 递归查找}break;}close(fd);
}int main(int argc, char *argv[])
{if(argc < 3){exit(0);}char target[512];target[0] = '/'; // 为查找的文件名添加 / 在开头strcpy(target+1, argv[2]);find(argv[1], target);exit(0);
}
xargs (moderate)
xargs介绍
整体思路:
- 将xargs命令传入的参数保存至指针数组,每个指针指向一个参数;
- 解析输入参数,如果遇到’ ’ 或者\n 就将参数保存至指针数组,每次读取完成一行就使用exec进行运行
- 最后一行进行单独判断运行,万一最后一行没有换行符
// xargs.c
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"void run(char *program , char ** args)
{if(fork() == 0 ){exec(program,args);exit(0);}return ;
}
int main(int argc,char* argv[]){char buf[2048]; // 读入时使用的内存池char *p = buf, *last_p = buf; // 当前参数的结束、开始指针char *argsbuf[128]; // 全部参数列表,字符串指针数组,包含 argv 传进来的参数和 stdin 读入的参数char **args = argsbuf; // 指向 argsbuf 中第一个从 stdin 读入的参数for(int i=1;i<argc;i++) {// 将 argv 提供的参数加入到最终的参数列表中*args = argv[i];args++;}char **pa = args;while(read(0,p,1) != 0 ){if(*p == ' ' || *p == '\n'){*p = '\0';*(pa++) = last_p;last_p = p+1;if(*p == '\n'){*pa = 0;run(argv[1],argsbuf);pa = args;}}p++;}if(pa != args) { // 如果最后一行不是空行// 收尾最后一个参数*p = '\0';*(pa++) = last_p;// 收尾最后一行*pa = 0; // 参数列表末尾用 null 标识列表结束// 执行最后一行指令run(argv[1], argsbuf);}while(wait(0) != -1) {}; exit(0);
}