MIT 6s081 lab 1.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];
        }
    }
}

在这里插入图片描述

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

结果:

$ 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/322870.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

解决jmeter响应乱码的问题

HTTP请求响应乱码 方法一&#xff1a;添加后置处理器BeanShell PostProcessor&#xff0c;写入【prev.setDataEncoding("utf-8")】 方法二&#xff1a;修改bin目录下的配置文件jmeter.properties&#xff0c;将配置修改为【sampleresult.default.encodingUTF-8】 J…

【LeetCode每日一题】82. 删除排序链表中的重复元素 II

2024-1-15 文章目录 [82. 删除排序链表中的重复元素 II](https://leetcode.cn/problems/remove-duplicates-from-sorted-list-ii/) 82. 删除排序链表中的重复元素 II 思路&#xff1a; 创建一个虚拟节点 dummy 作为头节点的前置节点。使用两个指针 pre 和 cur 分别指向前一个非…

光纤和光缆有何不同之处?

很多人会有这样的疑问&#xff0c;光纤和光缆有何不同之处&#xff1f;主要是因为光纤和光缆这两个名词容易引起混淆。在严格的定义下&#xff0c;光纤和光缆是两种不同的东西&#xff0c;然而在现实生活中&#xff0c;许多人仍然会混淆这两者。为了更好地理解光纤和光缆之间的…

全国博物馆数据, shp+excel数据,数据来源可靠,基于国家文物局发布的《2021年度全国博物馆名录》

数据名称: 全国博物馆数据 数据格式: shpexcel 数据几何类型: 点 数据坐标系: WGS84 数据来源&#xff1a;网络公开数据&#xff0c;数据名录来源于国家文物局发布的《2021年度全国博物馆名录》 数据字段&#xff1a; 序号字段名称字段说明1province省份名称2city城市名…

GoZero微服务个人探索之路(三)Go-Zero官方rpc demo示例探究

官方网址&#xff1a;https://go-zero.dev/docs/tasks/cli/grpc-demo 项目结构 demo包 两个文件均为protoc-gen-go-grpc自动生成构成一个完整的 gRPC 服务的定义和实现 democlient包 demo.go goctl生成的客户端代码 Request 和 Response 别名&#xff1a; 定义了 Request 和…

知识付费saas租户平台:揭秘成功的密码

明理信息科技知识付费saas租户平台 随着互联网的快速发展&#xff0c;人们越来越重视知识的获取和价值的挖掘。在这个信息爆炸的时代&#xff0c;知识付费已经成为了一种新的商业模式&#xff0c;为知识的传播和价值的转化提供了更加高效和便捷的途径。本文将探讨知识付费的发…

UI设计入门:解析用户界面的基础概念

UI设计是什么&#xff1a;用户界面设计的基础 用户的视觉体验。一个好的用户界面需要强大、可靠和良好的使用感。用户界面设计应尽量减少用户与产品互动的能量&#xff0c;使用户更容易实现目标。 以我们最熟悉的应用程序界面为例。通常&#xff0c;手机软件的UI用户界面设计…

LeetCode刷题---基本计算器

解题思路&#xff1a; 根据题意&#xff0c;字符串中包含的运算符只有和- 使用辅助栈的方法来解决该问题 定义结果集res和符号位sign(用于判断对下一数的加减操作),接着对字符串进行遍历。 如果当前字符为数字字符&#xff0c;判断当前字符的下一个字符是否也是数字字符&#x…

样式集-发布,发表页面完整代码及示意图

发表页面完整代码示意图 完整代码 wxml代码&#xff1a; <!--pages/publish/publish.wxml--> <view class"biaoqing" catchtap"showEmoji"><text>&#x1f60a;</text></view> <view class"fabiao" catchtap…

Jmeter分布式性能测试

在做后端服务器性能测试中&#xff0c;我们会经常听到分布式。哪你&#xff0c;是否了解分布式呢&#xff1f;今天&#xff0c;我们就来给大家讲讲&#xff0c;在企业实战中&#xff0c;如何使用分布式进行性能测试&#xff0c;实战过程中&#xff0c;又有哪些地方要特别注意&a…

引领安全创新 | 安全狗方案入选工信部《2023年工业和信息化领域数据安全典型案例名单》

近日&#xff0c;工业和信息化部网络安全管理局公布了2023年工业和信息化领域数据安全典型案例名单。 安全狗与厦门卫星定位应用股份有限公司、中移 (上海) 信息通信科技有限公司联合申报的智慧交通云数据安全与隐私保障典型案例也成功入选。 厦门服云信息科技有限公司&#xf…

感知器学习算法和Adaline规则

一.感知器的发展过程 感知器的发展可以追溯到20世纪50年代。它是一种简单的人工神经网络模型&#xff0c;最早由美国心理学家和计算机科学家弗兰克罗森布拉特&#xff08;Frank Rosenblatt&#xff09;于1957年提出。感知器的设计灵感来源于生物神经元的工作原理&#xff0c;旨…

Oracle全系列版本官网下载保姆及教程

Oracle全系列版本官网下载方法 下面以下载Oracle12cR2为例说明下载的整个过程。 基本步骤如下&#xff1a; 先注册一个Oracle账号并登录&#xff1b;进入到客户下载页面搜索要下载的数据库版本&#xff1b;得到Oracle下载器(Oracle_SSN_DML_xxxxx.exe)&#xff0c;注意&#xf…

Docker之Dockerfile构建镜像

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是君易--鑨&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的博客专栏《Docker之Dockerfile构建镜像》。&#x1f3af;&…

Shiro框架:ShiroFilterFactoryBean过滤器源码解析

目录 1.Shiro自定义拦截器SpringShiroFilter 1.1 ShiroFilterFactoryBean解析 1.1.1 实现FactoryBean接口 1.1.2 实现BeanPostProcessor接口 1.2 SpringShiroFilter解析 1.2.1 OncePerRequestFilter过滤逻辑实现 1.2.2 AbstractShiroFilter过滤逻辑实现 1.2.2.1 创建Sub…

网页测试遇到自动弹窗,Alert类无法处理?或许你该来学学这招了!

相信大家在使用selenium做网页自动化时&#xff0c;会遇到如下这样的一个场景&#xff1a; 在你使用get访问某一个网址时&#xff0c;会在页面中弹出如上图所示的弹出框。 首先想到是利用Alert类来处理它。 然而&#xff0c;很不幸&#xff0c;Alert类处理的结果就是没有结果…

矩阵快速幂算法总结

题目链接 活动 - AcWing 本课程系统讲解常用算法与数据结构的应用方式与技巧。https://www.acwing.com/problem/content/1305/ 题解 代码 #include <cstdio> #include <cstring> #include <iostream> #include <algorithm>using namespace std;type…

如何创作出优秀的电子邮件营销(EDM)?

EDM出现的时间很早&#xff0c;是非常传统的一种推广方式。即便是其他推广方式的蓬勃兴起&#xff0c;EDM依旧深受很多行业的喜爱。主要源于它极高的性价比&#xff0c;据可靠数据&#xff0c;EDM的投资回报比达1&#xff1a;48。 那一封优秀的EDM应该是怎么样的呢&#xff1f;…

一分钟带你了解--电商控价

电商行业发展至今带来了许多机遇&#xff0c;但同时也伴随着一些挑战。品牌电商在运营过程中&#xff0c;面临着诸如乱价、低价、窜货和假货等问题&#xff0c;这些问题不仅损害了品牌的形象和价值&#xff0c;也破坏了市场秩序&#xff0c;侵害了消费者的权益。 电商控价是解…