MIT 6s081 lab1:Xv6 and Unix utilities

这篇具有很好参考价值的文章主要介绍了MIT 6s081 lab1:Xv6 and Unix utilities。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Lab1: Xv6 and Unix utilities

作业网址:https://pdos.csail.mit.edu/6.828/2020/labs/util.html

Boot xv6(easy)

下载,启动xv6系统

$ git clone git://g.csail.mit.edu/xv6-labs-2020
Cloning into 'xv6-labs-2020'...
...
$ cd xv6-labs-2020
$ git checkout util
Branch 'util' set up to track remote branch 'util' from 'origin'.
Switched to a new branch 'util'
Build and run xv6:
$ make qemu
To quit qemu type: Ctrl-a x.

sleep(easy)

使用system call sleep.函数(在user/user.h中被声明)来完成

/*
    sleep.c
*/

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

int main(int argc, char *argv[])
{
    if(argc != 2) {
        printf("error, usage: <sleep> <sleep_time>\n");
        exit(1);
    }
    int sleep_time = atoi(argv[1]);

    /* 调用sleep函数 */
    sleep(sleep_time);

    exit(0);
}

pingpong(easy)

使用系统调用在一对管道上的两个进程之间“乒乓”一个字节,每个方向一个。父进程应该向子进程发送一个字节;子进程应打印“<pid>:received ping”,其中<pid>是其进程ID,将管道上的字节写入父进程,然后退出;父进程应该读取子进程的字节,打印“<pid>:received-pong”,然后退出。

/*
    pingpong.c
*/

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

int main(int argc, char *argv[])
{
    if(argc != 1) {
        printf("error, usage: <pingpong>\n");
        exit(1);
    }
    int pid;
    /* create pipe */
    int fd[2];
    if(pipe(fd) == -1) exit(1);

    pid = fork();
    char buf[10];

    if(pid == 0) /* child process */
    {
        int pid_this = getpid();
        char *str = "pong";
        read(fd[0], buf, sizeof(buf)); // 阻塞,等待父进程发送
        printf("%d: received %s\n", pid_this, buf);
        write(fd[1], str, strlen(str));
        close(fd[1]);
    }
    else{ /* parent process */
        int pid_this = getpid();
        char *str = "ping";
        write(fd[1], str, strlen(str));
        close(fd[1]);
        wait(0); //等待子进程退出,再去读取

        read(fd[0], buf, sizeof(buf));
        printf("%d: received %s\n", pid_this, buf);
    }
    exit(0);
}

primes (moderate)/(hard)

使用管道通信实现素数筛,求解35以内的素数。

此算法的核心如下:对于任何一级流水线而言,到达当前流水线级的最小数字一定是一个素数,因为在处理之前比它更小的数字时它没有被筛掉,在当前流水线下它也需要作为一个基数去筛掉它在现存数字中的所有倍数,将所有剩下来的数字送入下一级流水线,直到本级流水线只剩下一个数字时,整个筛法结束。

/*
    a concurrent version of prime sieve 并发版本的素数筛
    primes.c
*/

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

static const int N = 35;

int main(int argc, char *argv[])
{
    if(argc != 1) {
        printf("error, usage: <primes>\n");
        exit(1);
    }
    
    int left_fd[2];
    int right_fd[2];
    pipe(left_fd);
    int num = 2;
    for(num = 2; num <= N; num++) write(left_fd[1], &num, sizeof(num));

    while(1)
    {   
        if(fork()) {
            close(left_fd[1]); // 必须关闭父进程的写端
            close(left_fd[0]);
            wait(0);
            exit(0);
        }
        else {
            close(left_fd[1]); // 注意这里,必须关闭子进程的写端,当管道的2个写端都被关闭,read不会发生阻塞,没有数据直接返回
            pipe(right_fd);
            int x = -1, base = -1, cnt = 0;
            while(read(left_fd[0], &x, sizeof(num))){ // 子进程循环读入
                cnt++;
                if(base == -1){
                    base = x; // 第一个数必定是素数
                    printf("prime %d\n", base);
                }
                else{
                    if((x % base) != 0){ // 晒不掉的送入下一层
                        write(right_fd[1], &x, sizeof(num)); 
                    }
                }
                // printf("%d ", x);
            }
            
            if(cnt == 1) exit(0);
            // 这一层与子进程之间的管道是下一层中与父进程之间的管道!
            left_fd[0] = right_fd[0];
            left_fd[1] = right_fd[1];
        }
    }
}

MIT 6s081 lab1:Xv6 and Unix utilities,MIT6s081,c语言,risc-v,linux

find (moderate)

它可以在指定的目录下寻找指定名称的文件并打印出来,在编写代码之前可以从ls.c例程中学习如何读取目录信息,当深入到嵌套的文件夹中寻找时,应该使用递归写法。

文件信息结构体:

// 文件信息结构体
// 其中type表明了文件的类型是:文件、目录还是设备
#define T_DIR     1   // Directory
#define T_FILE    2   // File
#define T_DEVICE  3   // Device

struct stat {
  int dev;     // File system's disk device
  uint ino;    // Inode number
  short type;  // Type of file
  short nlink; // Number of links to file
  uint64 size; // Size of file in bytes
};

目录结构体:

// Directory is a file containing a sequence of dirent structures.
// 所谓目录,就是一系列dirent结构组成的顺序序列
#define DIRSIZ 14

struct dirent {
  ushort inum;
  char name[DIRSIZ];
};

/*
    find.c
*/

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

// 递归查找
void find(char *path, char *name)
{
    int fd;
    //关键在下面这两个结构体里面
    struct stat st; // 获取某个文件的状态
    struct dirent de; //获取目录下的所有项
    if((fd = open(path, 0)) < 0) {
        printf("cannot open %s\n", path);
        exit(1);
    }
    if(fstat(fd, &st) < 0){
        printf("cannot stat %s\n", path);
        close(fd);
        exit(1);
    }
    char buf[256]; // 这个数组不能开太大
    char *p;
    if(st.type == T_DIR)
    {
        if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){ // 防止路径太长
            printf("ls: path too long\n");
            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;
            // 根据前面的path,buf,p组合成当前项的绝对路径,获取其属性
            if(stat(buf, &st) < 0){
                printf("ls: cannot stat %s\n", buf);
                continue;
            }
            if(st.type == T_FILE) { //当前项是文件,则判断
                if(strcmp(de.name, name) == 0) printf("%s\n", buf);
            }
            else{
                // 当前项还是目录,则递归查找
                if(strcmp(de.name, ".") && strcmp(de.name, "..")) // 防止一直递归
                {
                    find(buf, name);
                }
            }
        }
    }
}

int main(int argc, char *argv[])
{
    if(argc != 3){
        printf("error, usage:<find> <path> <name>\n");
        exit(1);
    }
    // read directories
    char path[512], name[512];
    memcpy(path, argv[1], strlen(argv[1]));
    memcpy(name, argv[2], strlen(argv[2]));
    // printf("debug: path = %s\n",path);
    // printf("debug: name = %s\n",name);
    
    // 调用函数find
    find(path, name);

    exit(0);
}

xargs (moderate)

xargs(extensive arguments)是Linux系统中的一个很重要的命令,它一般通过管道来和其他命令一起调用,来将额外的参数传递给命令,这个小问题就是实现自己版本的xargs命令。初次理解xargs命令时还是有点费解的,指导书中的一个例子如下:

$ echo hello too | xargs echo bye
  bye hello too
$

要理解这个例子就将echo hello too | xargs看作一个整体,它本质上是将hello too传递给了xargs,然后经过xagrs的逻辑处理,就会将这些额外传入的参数交给后面的echo命令。我们的任务就是实现这里所谓的xargs的处理逻辑

/*
    xargs.c
*/
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/param.h"

char *g_argv[MAXARG];

int main(int argc, char *argv[])
{
    if(argc < 2) {
        printf("error, usage:<xargs> <program>\n");
        exit(1);
    }
    if(argc >= MAXARG) {
        printf("too much argv\n");
        exit(1);
    }
    char c;
    char buf[32];
     
    int i = 0, cnt = 0;
    for(; cnt < argc - 1; cnt++) { 
        g_argv[cnt] = (char*)malloc(sizeof buf); // 要先分配内存,野指针的解引用是未定义的结果
        // g_argv[cnt] = 0;
        // strcpy会对时针进行解引用,然后拷贝
        g_argv[cnt] = strcpy(g_argv[cnt], argv[cnt + 1]);        
    }

    while(read(0, &c, sizeof(c))) // 以字符形式读取完毕
    {
        cnt = argc - 1;
        if(c == '\n' || c == ' '){
            buf[i] = '\0'; // 结束符
            // printf("read: %s\n", buf);
            g_argv[cnt] = (char*)malloc(sizeof buf); // 分配内存
            g_argv[cnt] = strcpy(g_argv[cnt], buf);
            
            cnt++;
            i = 0;
            if(c == '\n') // 说明读完一行命令了,要让子进程去执行
            {   // 此时已经读取完标准输入的argv的
                if(cnt + (argc - 2) >= MAXARG - 1)
                {
                    printf("too much argv\n");
                    exit(1);
                }
                g_argv[cnt] = 0; // 最后一个命令必须为结束符
                if(fork())
                {   // 父进程等待子进程结束,等待完处理下一条命令
                    wait(0);
                }
                else{ // 子进程
                    exec(argv[1], g_argv);
                    exit(0);
                }
            }
        }
        else {
            buf[i++] = c; // 不是空格也不是换行符,那就读取字符
        }
    }
    
    exit(0);

}

调试方式

首先make qemu-gdb CPUS=1,启动gdb-server

然后在另一个终端中(启动gdb客户端)使用gdb-multiarch -q kernel/kernel(kernel/kernel表示要gdb的程序)

进入gdb后

  • b:设置断点
  • c:运行
  • n:单步运行
  • s:进入函数内部

使用gdb的layout split模式可以看到gdb要执行的下一条指令是什么,断点具体在什么位置

提交

创建time.txt,写入耗时

make grade

结果:文章来源地址https://www.toymoban.com/news/detail-796322.html

$ make qemu-gdb
sleep, no arguments: OK (2.8s) 
== Test sleep, returns == 
$ make qemu-gdb
sleep, returns: OK (0.4s) 
== Test sleep, makes syscall == 
$ make qemu-gdb
sleep, makes syscall: OK (1.0s) 
== Test pingpong == 
$ make qemu-gdb
pingpong: OK (1.0s) 
== Test primes == 
$ make qemu-gdb
primes: OK (1.1s) 
== Test find, in current directory == 
$ make qemu-gdb
find, in current directory: OK (1.0s) 
== Test find, recursive == 
$ make qemu-gdb
find, recursive: OK (1.1s) 
== Test xargs == 
$ make qemu-gdb
xargs: OK (1.1s) 
== Test time == 
time: OK 
Score: 100/100

到了这里,关于MIT 6s081 lab1:Xv6 and Unix utilities的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 6.s081/6.1810(Fall 2022)Lab5: Copy-on-Write Fork for xv6

    本来往年这里还有个Lazy Allocation的,今年不知道为啥直接给跳过去了。. 环境搭建 Lab1: Utilities Lab2: System calls Lab3: Page tables Lab4: Traps Lab5: Copy-on-Write Fork for xv6 官网链接 xv6手册链接,这个挺重要的,建议做lab之前最好读一读。 xv6手册中文版,这是几位先辈们的辛勤奉献来的呀!

    2024年02月14日
    浏览(25)
  • 1670_MIT 6.828 xv6中增加系统调用的实现与分析

             全部学习汇总: GreyZhang/g_unix: some basic learning about unix operating system. (github.com)          操作系统的任务调度切换,在xv6中其实是基于中断的方式来进行触发的。          在ttap处理的部分,调用了一个trap处理的C语言接口。          也就是上面的接口。

    2023年04月11日
    浏览(26)
  • mit 6.824 lab1分析

    略 map阶段每个worker应该把中间文件分成nReduce份,nReduce是reduce任务的数量 worker完成reduce任务后生成文件名 mr-out-X mr-out-X 文件每行应该是 \\\"%v %v\\\" 格式,参考 main/mrsequential.go worker处理完map任务,应该把生成的中间文件放到当前目录中,便于worker执行reduce任务时读取中间文件 当所

    2023年04月10日
    浏览(40)
  • MIT 6.S081 Lab Three

    本文为 MIT 6.S081 2020 操作系统 实验三解析。 MIT 6.S081课程前置基础参考: 基于RISC-V搭建操作系统系列 在本实验中,您将探索页表并对其进行修改,以简化将数据从用户空间复制到内核空间的函数。 开始编码之前,请阅读xv6手册的第3章和相关文件: * kernel/memlayout.h* ,它捕获了

    2024年02月09日
    浏览(33)
  • 【MIT 6.S081】Lab7: Multithreading

    本Lab比较简单,就是为xv6添加一个用户级的多线程功能,然后熟悉一下Linux下多线程编程。 笔者用时约2h 这一部分的代码不涉及内核代码,所以也比较简单,根据提示修改 user/uthread.c 中的代码即可。仿照内核中进程转换函数 swtch 的实现即可。首先,添加一个 context 上下文结

    2023年04月09日
    浏览(25)
  • MIT6.5830 Lab1-GoDB实验记录(五)

    完成了Exercise 1,还有四个Exercise在等着我,慢慢来吧。 实验准备 了解缓冲池 缓冲池,俗称BP。相关的概念还有数据页和缓存页。页(Pages)的概念和操作系统中“分页”的概念是一样的,指的都是把逻辑地址空间分为若干同等大小的页,并从0开始编号。 而缓冲池(Buffer Po

    2024年02月05日
    浏览(36)
  • MIT6.5830 Lab1-GoDB实验记录(四)

    标签:Golang 读写缓冲区我是一点思路都没有,所以得单独开篇文章记录。 实验补充 了解buffer、序列化与反序列化 这里的序列化,简单来说类似于把一个很长的字符串拆成一个个字符;反序列化就是把这一个个字符拼回成完整的字符串。此处我们需要根据所给的Tuple,转换为

    2024年02月06日
    浏览(35)
  • MIT6.S081 - Lab2: system calls

    step1:系统调用声明 user/user.h :系统调用函数(如 int fork(void) ) step2:ecall 进入内核态 user/usys.S (该文件由 user/usys.pl 生成,后续添加函数可以在这里添加):执行如下命令 将系统调用的编号(在 kernel/syscall.h 中定义)写入 a7 寄存器 从 ecall 进入中断处理函数 step3:保存数据并

    2024年04月23日
    浏览(34)
  • Lab1 Packet Sniffing and Spoofing Lab

    @[TOC] Packet Sniffing and Spoofing Lab 实验网站连接link 1.先在虚拟机上导入 SEED VM并完成相应的配置。配置可以参考:link 2.使用准备好的docker-compose.yml去配置虚拟机环境 2.1先把docker-compose.yml放到虚拟机的某个文件夹下。 2.2 然后再文件所在的目录下输入命令运行 docker-compose up -d就能

    2024年02月07日
    浏览(41)
  • MIT6.S081 - Lecture1: Introduction and Examples

    理解操作系统的设计和实现 通过 XV6 操作系统动手实验,可以扩展或改进操作系统 Abstraction: 对硬件进行抽象 Multiplex: 在多个应用程序之间共用硬件资源 Isolation: 隔离性,程序出现故障时,不同程序之间不能相互干扰 Sharing: 实现共享,如数据交互或协同完成任务 Securi

    2024年04月15日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包