【MIT 6.S081】Lab1: Xv6 and Unix utilities


本Lab包括五个简单程序的实现,初步熟悉系统调用接口。
笔者用时约6h(我太菜辣)

概述

根据文档说明,我们需要把写的每个程序文件放在user文件夹下,并且在MakeFileUPROGS添加相应的程序名,这样子就可以在qemu中直接用命令行指令调用相应的程序啦。如下图所示。
在这里插入图片描述

sleep

sleep程序接收一个参数,调用库函数sleep进行睡眠即可,没啥好说滴。

#include "kernel/types.h"
#include "user/user.h"

int main(int argc, char* argv[]) 
{
    if (argc != 2) { 
        fprintf(2, "usage: sleep time\n");
        exit(1);
    }

    sleep(atoi(argv[1]));

    exit(0);
}

pingpong

该程序的要求就是创建一个子进程,父进程为子进程发送一个字节,子进程接收到字节之后,打印ping,向父进程发送一个字节,父进程接收到该字节之后打印pong,程序结束。
做法就是打开两个管道,一个用于父进程写子进程读,另一个则相反。

#include "kernel/types.h"
#include "user/user.h"

int main(int argc, char* argv[]) 
{
    if (argc != 1) { 
        fprintf(2, "usage: pingpong\n");
        exit(1);
    }

    int p_father_to_child[2];
    int p_child_to_father[2];
    if (pipe(p_father_to_child) == -1 || pipe(p_child_to_father) == -1) {
        fprintf(2, "failed to open pipe\n");
        exit(1);
    }

    char buf = 'C';
    int exit_state = 0;

    // child process
    if (fork() == 0) {
        close(p_father_to_child[1]);
        close(p_child_to_father[0]);

        if (read(p_father_to_child[0], &buf, 1) != 1) {
            fprintf(2, "failed to read pipe in child\n");
            exit_state = 1;
        }

        fprintf(1, "%d: received ping\n", getpid());

        if (write(p_child_to_father[1], &buf, 1) != 1) {
            fprintf(2, "failed to write pipe in child\n");
            exit_state = 1;
        }

        exit(exit_state);
    }

    // father process
    close(p_father_to_child[0]);
    close(p_child_to_father[1]);

    if (write(p_father_to_child[1], &buf, 1) != 1) {
        fprintf(2, "failed to write pipe in parent\n");
        exit_state = 1;
    }

    wait(0);
    
    if (read(p_child_to_father[0], &buf, 1) != 1) {
        fprintf(2, "failed to read pipe in parent\n");
        exit_state = 1;
    }

    fprintf(1, "%d: received pong\n", getpid());

    exit(exit_state);
}

primes

该题目要求我们写一个并发运行的素数筛,其思想大致如下:
建立若干个进程,前一个进程的输出为后一个进程的输入(用管道进行连接);
第一个进程没有输入,向管道直接输出2~35;第二个进程以第一个进程的输出作为输入(2~35),向终端输出2(素数),并筛掉2的倍数,向下一个管道输出非2倍数的数,以此类推,直到没有数输出为止即可。

#include "kernel/types.h"
#include "user/user.h"

#define RD 0
#define WR 1
const uint INT_SIZE = sizeof(int);

void getprime(int left_p[]) {
    // check if there has a prime
    int prime, i;
    if (read(left_p[RD], &prime, INT_SIZE) == 0) {
        close(left_p[RD]);
        exit(0);
    }

    fprintf(1, "prime %d\n", prime);
    
    // create the pipe to the right neighbor
    int right_p[2];
    pipe(right_p);

    // read the data from left neighbor
    while (read(left_p[RD], &i, INT_SIZE) != 0) {
        if (i % prime != 0) 
            write(right_p[WR], &i, INT_SIZE);
    }
    close(left_p[RD]);
    close(right_p[WR]);

    if (fork() == 0) {
        getprime(right_p);
    } else {
        close(right_p[RD]);
        wait(0);
    }

}

int main(int argc, char* argv[]) 
{
    if (argc != 1) { 
        fprintf(1, "usage: primes\n");
        exit(1);
    }

    int p[2], i;
    pipe(p);

    for (i = 2; i <= 35; i ++ ) {
        write(p[WR], &i, INT_SIZE);
    }
    close(p[WR]);

    if (fork() == 0) {
        getprime(p);
    } else {
        close(p[RD]);
        wait(0);
    }

    exit(0);
}

find

该题目要求写一个程序,在指定的目录中查找与给定文件名相同的文件,若查找到了则输出文件路径(从指定目录开始的路径)。
根据提示,查看user/ls.c文件中ls程序的实现,其中使用fstat函数查看当前路径的类型(目录或文件)
类似的,我们实现的文件查找也使用fstat函数判断路径类型,如果当前路径是文件,则与给定的文件名比较(相等则输出);如果当前路径是目录,则遍历目录中的内容进行递归查找(注意不要对...递归)

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"

void find_file(char* path, const char* filename) {
    struct dirent de;
    struct stat st;
    int fd;
    char buf[512], *p;

    if ((fd = open(path, 0)) < 0) {
        fprintf(2, "find: cannot open %s\n", path);
        exit(1);
    }

    if (fstat(fd, &st) < 0) {
        fprintf(2, "find: cannot stat %s\n", path);
        close(fd);
        exit(1);
    }

    if (st.type == T_FILE) {
        fprintf(2, "usgae: find <directory> <filename>\n");
        close(fd);
        exit(1);
    }
    
    if (strlen(path) + 1 + DIRSIZ + 1 > sizeof buf) {
        fprintf(2, "find: path too long\n");
        close(fd);
        exit(1);
    }

    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) {
            fprintf(2, "find: cannot stat %s\n", buf);
            continue;
        }

        if (st.type == T_FILE) {
            if (strcmp(p, filename) == 0) 
                fprintf(1, "%s\n", buf);
        } else if (st.type == T_DIR && strcmp(p, ".") != 0 && strcmp(p, "..") != 0) {
            find_file(buf, filename);
        }

    }
    close(fd);
}

int main(int argc, char* argv[]) 
{

    if (argc != 3) {
        fprintf(2, "usgae: find <directory> <filename>\n");
        exit(1);
    }

    find_file(argv[1], argv[2]);

    exit(0);
}

xargs

该题要求实现一个程序,每从标准输入中读取一行,就调用一次命令(参数是命令)。
这个其实不难(但是我一开始把buf开大了,调了一个小时),大概就是从标准输入读取一行,然后分割成若干个命令行参数,创建一个子进程后使用ecex执行命令即可。

#include "kernel/types.h"
#include "kernel/param.h"
#include "user/user.h"

#define LINE 1024

int read_line(int fd, char* buf, int len) {
    char* p = buf;
    int i = 0;

    while (i < len - 1) {
        int res = read(fd, p, 1);
        if (res == -1) return -1;
        else if (res == 0)
            break;

        p ++;
        i ++;
        if (*(p - 1) == '\n') break;
    }
    *p = 0;
    return i;
}

int main(int argc, char* argv[])
{
    if (argc == 1) {
        fprintf(2, "usage: xargs command [args]\n");
        exit(1);
    }

    char* args[MAXARG + 1];
    memset(args, 0, sizeof args);
    int args_num = argc - 1;
    int i, j, k;

    for (i = 1; i < argc; i ++ )
        args[i - 1] = argv[i];        

    char buf[LINE];
    int res;

    while ( (res = read_line(0, buf, LINE)) != 0 ) {
        if (res == -1) {
            fprintf(2, "xargs: cannot read line from standard input\n");
            exit(1);
        }

        if (buf[res - 1] != '\n' && res == LINE) {
            fprintf(2, "xargs: line too long\n");
            exit(1);
        }

        // split the args
        buf[-- res] = '\0';
        i = 0, k = args_num;
        while (i < res) {
            if (buf[i] == ' ') i ++;
            else {
                j = i;
                while (j < res && buf[j] != ' ')
                    j ++;

                // buf[i ~ j - 1] is an argument
                if (k == MAXARG) {
                    fprintf(2, "xargs: too many arguments\n");
                }
                args[k] = (char*)malloc(sizeof(char) * (j - i + 1));
                memcpy(args[k], buf + i, j - i);
                args[k][j - i + 1] = '\0';
                k ++;

                i = j;
            }
        }

        if (fork() == 0) {
            exec(args[0], args);
            fprintf(2, "xargs: command %s not found\n", args[0]);
        }

        wait(0);
        // free the malloc space
        for (i = args_num; i < k; i ++ ) 
            free(args[i]), args[i] = 0;
    }

    exit(0);
}

### 回答1:xv6是一个基于Unix操作系统,它是一个教学用途的操作系统,旨在教授操作系统的基本概念和实现。它是在MIT的x86架构上开发的,包括了Unix的一些基本功能,如进程管理、文件系统、内存管理等。xv6的源代码是公开的,可以用于学习和研究。 Unix utilitiesUnix操作系统中的一些基本工具,如ls、cd、cp、mv、rm等。这些工具可以帮助用户管理文件和目录,执行各种操作。这些工具的实现是基于Unix的系统调用,可以通过编写C程序来调用这些系统调用实现相应的功能。这些工具是Unix操作系统的基础,也是其他操作系统的参考。 ### 回答2: lab: xv6 and unix utilities 实验是一项旨在帮助学生深入理解操作系统Unix 工具使用的实验。该实验分为两个部分,第一部分教授学生如何构建和运行 xv6 操作系统;第二部分则重点教授 Unix 工具的使用。 在 xv6 操作系统部分,学生将学习到操作系统内核的基本结构和实现原理。实验将引导学生理解内存管理、进程调度、系统调用等关键操作系统概念。此外,学生还将学习如何编写简单的 shell 以及如何通过修改 xv6 内核代码来实现新的系统调用和功能。 在 Unix 工具部分,学生将探索 Unix 系统中广泛使用的常见工具。这些工具包括 vi 编辑器、grep、awk、sed 等。实验将介绍这些工具的基本使用方法以及它们在处理文本和数据时的实际应用。这部分实验还将让学生深入了解 shell 和 shell 脚本的编写,帮助他们在 Unix 环境中轻松地编写脚本和自动化任务。 lab: xv6 and unix utilities 实验对计算机科学专业的学生具有重要意义。通过完成这个实验,学生将建立起对操作系统Unix 工具的深入理解,为他们成为一名优秀的软件工程师奠定坚实的基础。同时,这个实验还将为学生提供实践经验,让他们能够将所学知识应用到真实的软件开发和运维中。 ### 回答3: Lab: xv6 and Unix Utilities是一个计算机科学领域的实验,旨在让学生深入了解Unix操作系统以及操作系统本身的自我管理机制。在这个实验中,学生需要从零开始构建一个类似于Unix操作系统,在这个操作系统中,学生需要设计一些基本命令,例如ls,cat,grep等等,并且将它们与系统的底层API结合起来,以实现各种功能。此外,学生还需要了解和探索xv6这个开发工具,它是一个轻量级基于Unix操作系统实现,具有一定的可移植性和简洁性,因此,它可以作为一个基础框架来实现一个完整的Unix操作系统。 这个实验的目标是让学生了解Unix的基本命令结构和API,以及操作系统内部的一些基本机制,例如进程管理,文件系统交互以及进程通信等等。此外,通过实现这些命令,学生还可以学到一些基本的C语言编程技能,例如文件操作,字符串处理以及进程管理等等。还可以学习到如何使用Git等版本控制工具,以及如何进行调试和测试代码的技巧。 在整个实验过程中,学生需要有较强的自我管理能力和综合运用能力,因为在实现这些命令的同时,他们还需要和其他团队成员进行交流和合作,以及不断改进和完善他们的代码。总之,这个实验是一个非常有趣且富有挑战性的计算机科学课程,通过完成这个实验,学生可以更好地了解操作系统的构造和运作机制,以及如何设计和开发高效的系统级应用程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值