文章

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;
}
License:  CC BY 4.0