MIT 6.S081 | Lab1:Xv6 and Unix utilities
课程地址:https://pdos.csail.mit.edu/6.S081/2020/schedule.html 代码地址:https://github.com/36-H/xv6-labs-2020/tree/util
Lab 1: Unix utilities
启动xv6(难度:Easy)
获取代码并切换到util分支
git clone git://g.csail.mit.edu/xv6-labs-2020
cd xv6-labs-2020
git checkout util
make qemu
sleep(难度:Easy)
YOUR JOB 实现xv6的UNIX程序sleep:您的sleep应该暂停到用户指定的计时数。一个滴答(tick)是由xv6内核定义的时间概念,即来自定时器芯片的两个中断之间的时间。您的解决方案应该在文件user/sleep.c中
Step 1
在Makefile文件中添加
$U/_grind\
$U/_wc\
$U/_zombie\
$U/_sleep\ //添加这一行
ifeq ($(LAB),syscall)
Step 2
创建user/sleep.c文件
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc, char *argv[]){
if(argc < 1 || argc > 2){
fprintf(2, "usage: sleep <ticks>\n");
exit(1);
}
sleep(atoi(argv[1]));
exit(0);
}
##pingpong(难度:Easy)
YOUR JOB 编写一个使用UNIX系统调用的程序来在两个进程之间“ping-pong”一个字节,请使用两个管道,每个方向一个。父进程应该向子进程发送一个字节;子进程应该打印“
: received ping”,其中 是进程ID,并在管道中写入字节发送给父进程,然后退出;父级应该从读取从子进程而来的字节,打印“ : received pong”,然后退出。您的解决方案应该在文件user/pingpong.c中。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(){
// [0] 读取 [1] 写入
int pipe1[2],pipe2[2];
//pipe1: 父 -> 子
//pipe2: 子 -> 父
// 父 子
// read pipe2[0] pipe1[0]
// write pipe1[1] pipe2[1]
pipe(pipe1);
pipe(pipe2);
if(fork() != 0){
write(pipe1[1],"1",1);
char buf;
read(pipe2[0],&buf,1);
printf("%d: received pong\n", getpid());
wait(0);
}else{
char buf;
read(pipe1[0],&buf,1);
printf("%d: received ping\n", getpid());
write(pipe2[1],"1",1);
}
exit(0);
}
##Primes(素数,难度:Moderate/Hard)
YOUR JOB 使用管道编写prime sieve(筛选素数)的并发版本。这个想法是由Unix管道的发明者Doug McIlroy提出的。请查看这个网站,该网页中间的图片和周围的文字解释了如何做到这一点。您的解决方案应该在user/primes.c文件中。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
__attribute__((noreturn))
void prime(int input_pipe[]);
int main(){
//创建管道
int input_pipe[2];
pipe(input_pipe);
//子进程
if(fork() == 0){
//关闭写
close(input_pipe[1]);
prime(input_pipe);
exit(0);
}else{
//关闭读
close(input_pipe[0]);
int i;
//将 2 ~ 35 的数全部写入子进程 主进程不进行筛选
for(i = 2; i <= 35; i++){
write(input_pipe[1],&i,sizeof(i));
}
//终结符
i = -1;
write(input_pipe[1],&i,sizeof(i));
}
wait(0);
exit(0);
}
void prime(int input_pipe[]){
int cur;
read(input_pipe[0], &cur, sizeof(cur));
if(cur == -1) {
exit(0);
}
printf("prime %d\n", cur);
int right_pipe[2];
pipe(right_pipe);
//子进程
if(fork() == 0){
close(right_pipe[1]);
close(input_pipe[0]);
prime(right_pipe);
}else{
//关闭读
close(right_pipe[0]);
int buf;
//读输入管道数据
while(read(input_pipe[0], &buf, sizeof(buf)) && buf != -1){
if(buf % cur != 0){
write(right_pipe[1],&buf,sizeof(buf));
}
}
//终结符
buf = -1;
write(right_pipe[1],&buf,sizeof(buf));
wait(0);
exit(0);
}
}
###find(难度:Moderate)
YOUR JOB 写一个简化版本的UNIX的find程序:查找目录树中具有特定名称的所有文件,你的解决方案应该放在user/find.c
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
void find(char *path,char *file_name);
int main(int argc, char *argv[]){
if(argc != 3){
fprintf(2, "usage: find <path> <filename>\n");
exit(1);
}
char file_name[512];
file_name[0] = '/'; // 为查找的文件名添加 / 在开头
strcpy(file_name + 1, argv[2]);
find(argv[1], file_name);
exit(0);
}
void find(char *path,char *file_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:
// 如果文件名结尾匹配 `/target`,则视为匹配
if(strcmp(path + strlen(path) - strlen(file_name), file_name) == 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){
continue;
}
memmove(p, de.name, DIRSIZ);
p[DIRSIZ] = 0;
if(stat(buf, &st) < 0){
printf("find: cannot stat %s\n", buf);
continue;
}
// 不进入 `.` 和 `..`
if(strcmp(buf + strlen(buf) - 2, "/.") != 0 && strcmp( buf + strlen(buf) - 3, "/..") != 0) {
find(buf, file_name); // 递归查找
}
}
break;
}
close(fd);
}
##xargs(难度:Moderate)
YOUR JOB 编写一个简化版UNIX的xargs程序:它从标准输入中按行读取,并且为每一行执行一个命令,将行作为参数提供给命令。你的解决方案应该在user/xargs.c
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/param.h"
#include "kernel/fs.h"
char* readLine();
int main(int argc, char *argv[]){
if(argc < 2) {
printf("Usage: xargs [command]\n");
exit(-1);
}
//获取参数
char *args_buffer[MAXARG];
char **args = args_buffer;
//在 char *argv[] 中 argv[0] 为执行的命令 而参数从argv[1]开始填充
for(int i = 1; i < argc;i++){
*args = argv[i];
// printf("%s\n", argv[i]);
args++;
}
char *line;
char **start = args; //每读完一行要重置参数列表
//读取一行
while((line = readLine()) != 0){
args = start;
//进行参数拼接 也就是对line 进行 spilte 操作 拼接到args_buffer
char *cur = line; //遍历指针
char *buf = malloc(64); //单个参数缓冲区
char *buf_ptr = buf;
while(*cur != 0){
if(*cur == ' ' && buf != buf_ptr){ //说明有参数且读到空格
*buf_ptr = 0;
*args = buf;
args++;
buf = malloc(64);
buf_ptr = buf;
}else{
*buf_ptr = *cur;
buf_ptr++;
}
cur++;
}
if(buf != buf_ptr){
*args = buf;
args++;
}
*args = 0;
free(line);
//执行
if(fork() == 0){
// for(char **p = args_buffer;*p != 0; p++){
// printf("%s\n",*p);
// }
exec(argv[1], args_buffer);
}else{
wait(0);
}
}
exit(0);
}
char* readLine(){
char* buf = malloc(512);
char* cur = buf;
// 我们会从标准输入流按行读取 fd = 0 为标准输入流
// 每次读取一个字节
while(read(0,cur,1) != 0){
//读取结束
if(*cur == '\0' || *cur == '\n'){
*cur = '\0';
return buf;
}
cur++;
}
if(cur == buf){
free(buf);
return 0;
}
return buf;
}