ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[MIT 6.S081] Lab1: Xv6 and Unix utilities

2021-09-21 00:02:12  阅读:413  来源: 互联网

标签:int utilities argv Xv6 Lab1 fd close include buf


[MIT 6.S081] Lab1: Xv6 and Unix utilities

Boot xv6 (easy)

步骤

  1. 获取实验用的 xv6 源码并切换到 util 分支
$ git clone git://g.csail.mit.edu/xv6-labs-2020
$ cd xv6-labs-2020
$ git checkout util

在这里插入图片描述
2. 构建并运行 xv6

$ make qemu

在这里插入图片描述
3. 在 xv6 中使用命令测试
使用 ls 列出文件:

$ ls

在这里插入图片描述
使用 Ctrl+p 查看运行进程(xv6 无 ps 命令)
在这里插入图片描述
使用 Ctrl+a x 退出 QWMU.
在这里插入图片描述

  • 注: 先按 Ctrl+a, 再按 x键.

sleep (easy)

步骤

  1. 编写 sleep.c
    此处sleep考虑了输入参数不为数字的情况. 此外, 对于sleep后多个参数的情况也进行了考虑, 但是发现在 QEMU 中执行是失败的, 测试是通过的, 可以推测此处无需考虑多个参数的情况.
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

int main(int argc, char *argv[]) {
	int ticks;
	// 需要1个参数
	if(argc != 2) {
		fprintf(2, "Usage: sleep number\n");
		exit(1);
	}
	ticks = atoi(argv[1]);
	// 判断
	if(ticks == 0 && strcmp(argv[1],"0") != 0){
		fprintf(2, "sleep: invalid time interval '%s'\n", argv[1]);
		exit(2);
	}
	sleep(ticks);
	exit(0);
}
  1. 修改 Makefile 文件中的 UPROGS 部分:
    在这里插入图片描述

测试

在这里插入图片描述

pingpong (easy)

步骤

  1. 编写代码
    该代码主要是管道 pipe 的应用, 注意管道是单工的, 因此父子进程相互通信需要创建两个管道.
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

int main() {
    // fd1用于父进程向子进程通信, fd2反之
	int fd1[2], fd2[2];
	char buf[1];
	pipe(fd1);
	pipe(fd2);
	if(fork() == 0){
		close(fd1[1]);
		read(fd1[0],buf,1);
		close(fd1[0]);
		printf("%d: received ping\n",getpid());
		
		close(fd2[0]);
		write(fd2[1],buf,1);
		close(fd2[1]);
	} else {
		close(fd1[0]);
		write(fd1[1],"a",1);
		close(fd1[1]);
		
		close(fd2[1]);
		read(fd2[0],buf,1);
		close(fd2[0]);
		printf("%d: received pong\n", getpid());
	}
	exit(0);
}
  1. 修改 Makefile 文件中的 UPROGS 部分:
    在这里插入图片描述

测试

在这里插入图片描述
在这里插入图片描述

primes (M/hard)

步骤

  1. 编写代码
    在这里插入图片描述
    质数的求解方法本质上就是经典的"筛法", 此实验中通过多个进程来达到并行处理. 进程相当于上图的芯片, 而用于进程间通信的管道就模拟导线进行数字传递. 下面算法中, 个人使用了递归来达到构建迭代的子进程的目的.
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

int subProcess(int *oldFd) {
    // 关闭原管道写端
    close(oldFd[1]);
    int fd[2];
    int prime;
    int num;
    // 若能从原管道读到数据
    if (read(oldFd[0], &prime, 4)) {
        // 第一个数据为质数,进行输出
        printf("prime %d\n", prime);
        // 创建管道和子进程
        pipe(fd);
        if (fork() == 0) {    //子进程
            // 递归调用
            subProcess(fd);
        } else {    // 父进程
            // 关闭新管道读端
            close(fd[0]);
            // 从原管道进行读取
            while (read(oldFd[0], &num, 4)) {
                // 不能被记录的质数整除则写入新管道
                if (num % prime != 0) {
                    write(fd[1], &num, 4);
                }
            }
            // 此时父进程的原管道关闭, 则关闭原管道的读端
            close(oldFd[0]);
            // 关闭新管道的写端
            close(fd[1]);
            // 等待子进程结束
            wait((int *) 0);
        }
    } else {    // 此时说明原管道已关闭,第一个数字都读不出
        // 不创建子进程直接关闭原管道读端
        close(oldFd[0]);
    }
    exit(0);
}

int main() {
    int i;
    int fd[2];
    pipe(fd);
    if (fork() == 0) {
        subProcess(fd);
    } else {    // 父进程
        close(fd[0]);
        // 遍历 2~35 写入管道写端
        for (i = 2; i <= 35; ++i) {
            write(fd[1], &i, 4);
        }
        // 写完关闭管道写端并等待子进程结束
        close(fd[1]);
        wait((int *) 0);
    }
    exit(0);
}
  1. 修改 Makefile 文件中的 UPROGS 部分

测试

在这里插入图片描述
在这里插入图片描述

find (moderate)

步骤

  1. 编写代码
    代码思路主要参考 user/ls.c, 需要使用 struct direntstruct stat 两个和文档相关的结构体. 此处对于文件夹同样是利用递归来进行查找.
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"

void find(char *dir, char *file) {
    char buf[512] = {0}, *p;
    int fd;
    struct dirent de;
    struct stat st;
    // 打开目录
    if ((fd = open(dir, 0)) < 0) {
        fprintf(2, "find: cannot open %s\n", dir);
        return;
    }
    // 判断路径长度
    if (strlen(dir) + 1 + DIRSIZ + 1 > sizeof(buf)) {
        fprintf(2, "find: path too long\n");
        close(fd);
        return;
    }

    strcpy(buf, dir);
    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_DIR) {
            find(buf, file);
        } else if (strcmp(de.name, file) == 0) {    //是文件则进行比较, 若与查找一致则输出
            printf("%s\n", buf);
        }
    }
    close(fd);
    return;
}

int main(int argc, char *argv[]) {
    struct stat st;
    
    if (argc != 3) {
        fprintf(2, "Usage: find dir file\n");
        exit(1);
    }
    // 获取查找目录的状态并判断是否为目录
    if (stat(argv[1], &st) < 0) {
        fprintf(2, "find: cannot stat %s\n", argv[1]);
        exit(2);
    }
    if (st.type != T_DIR) {
        fprintf(2, "find: '%s' is not a directory\n", argv[1]);
        exit(3);
    }
    find(argv[1], argv[2]);
    exit(0);
}
  1. 修改 Makefile 文件中的 UPROGS 部分

测试

在这里插入图片描述
在这里插入图片描述

xargs (moderate)

步骤

  1. 编写代码
    该部分实验耗时较长, 重点之一在于 xargs 指令的用法, 详见 命令教程 - 阮一峰的网络日志, 其中读取输入是通过标准输入 stdin 进行的, 在代码上体现为 read() 函数.
    另一方面是参数的解析, 下面代码考虑了 xargs 后面无参数时默认以 echo 作为指令. 此外, 另一个比较困难的地方在于"空格",“回车”,以及"输入结束"三种情况的处理. 网上有些代码没有考虑输入结束, 也就是 read() 返回 0 的情况.
    具体说来, 本题个人主要想到有以下两个方法:
  • 第 1 种方法是对"空格",“回车”,以及"输入结束"三种情况都进行处理. 也就是在读取输入时会根据空格将参数进行划分.
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/param.h"

#define MAXLEN 32

int main(int argc, char *argv[]) {
    // 执行命令
    char *path = "echo";
    // 存放参数的数组和指针
    char buf[MAXLEN * MAXARG] = {0},*p;
    // 参数的指针数组
    char *params[MAXARG];
    // xargs后命令所带的参数个数
    int oriParamCnt = 0;
    // 参数序号
    int paramIdx;
    // 参数长度
    int paramLen;
    int i;
    // 临时记录读取字符
    char ch;
    // read读取长度, 用于判断是否输入结束
    int res;

    // 参数数量大于1
    if (argc > 1) {
        // 提取指令
        path = argv[1];
        // 设置参数, 注意也需要带上指令的参数
        for (i = 1; i < argc; ++i) {
            params[oriParamCnt++] = argv[i];
        }
    } else {    //参数唯一,即只有xargs
        // 即指令为echo
        params[oriParamCnt++] = path;
    }
    
    // 后续参数起始序号
    paramIdx = oriParamCnt;
    p = buf;
    paramLen = 0;
    while (1) {
        res = read(0, p, 1);
        ch = *p;
        
        // 若读取的为一般字符
        if (res != 0 && ch != ' ' && ch != '\n') {
            ++paramLen;
            ++p;
            continue;
        }
        // 未读取成功, 或者读取的是空格或回车
        // 计算参数起始位置
        params[paramIdx++] = p - paramLen;
        // 参数长度置0
        paramLen = 0;
        // 设置字符结束符
        *p = 0;
        ++p;
        // 若读取的参数超过上限
        if (paramIdx == MAXARG && ch == ' ') {
            // 一直读到回车即下个命令为止
            while ((res = read(0, &ch, 1)) != 0) {
                if (ch == '\n') {
                    break;
                }
            }
        }
        // 若读取的不为空格, 即 res==0||ch=='\n'
        if (ch != ' ') {
            // 创建子进程执行命令
            if (fork() == 0) {
                exec(path, params);
                exit(0);
            } else {
                // 父进程等待子进程
                wait((int *) 0);
                // 重置参数序号和指针
                paramIdx = oriParamCnt;
                p = buf;
            }
        }
        // 若输入读取完毕则退出
        if (res == 0) {
            break;
        }
    }
    exit(0);
}
  • 第 2 种方法仅对"回车"以及"输入结束"两种种情况都进行处理. 也就是在读取输入时不根据空格将参数进行划分, 直接作为 1 个参数, 实际测试证明确实是可行的, 空格应该会被 exec() 执行的命令进行解析. 代码参考了 MIT 6.S081 2020 LAB1记录 - 知乎 中的 xargs 代码.
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/param.h"

#define MAXLEN 32

int main(int argc, char *argv[]) {
    char *path = "echo";
    char buf[MAXLEN * MAXARG] = {0}, *p;
    char *params[MAXARG];
    int paramIdx = 0;
    int i;
    int len;

    // 读取原有参数, 与上一种方法相同
    if (argc > 1) {
        if (argc + 1 > MAXARG) {
            fprintf(2, "xargs: too many ars\n");
            exit(1);
        }
        path = argv[1];
        for (i = 1; i < argc; ++i) {
            params[paramIdx++] = argv[i];
        }
    } else {
        params[paramIdx++] = path;
    }
    
    p = buf;
    while (1) {
        // 读取字符知道结束或遇到回车
        while (1) {
            len = read(0, p, 1);
            if (len == 0 || *p == '\n') {
                break;
            }
            ++p;
        }
        *p = 0;
        // 直接将其作为一个参数
        params[paramIdx] = buf;
        // 创建子进程执行
        if (fork() == 0) {
            exec(path, params);
            exit(0);
        } else {
            wait((int *) 0);
            p=buf;
        }
        if (len == 0) {
            break;
        }
    }
    exit(0);
}
  1. 修改 Makefile 文件中的 UPROGS 部分

测试

在这里插入图片描述
在这里插入图片描述

标签:int,utilities,argv,Xv6,Lab1,fd,close,include,buf
来源: https://blog.csdn.net/LostUnravel/article/details/120397205

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有