这里给出Lab1 Utilities的解析,由于对很多linux函数不太熟悉,所以还是花了比较多的时间。

学习资料:

参考资料:

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