MIT 6s081 lab1:Xv6 and Unix utilities

news2024/9/24 11:27:35

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Maxwell数据同步(增量)

1. Maxwell简介 1.1 Maxwell概述 Maxwell 是由美国Zendesk公司开源&#xff0c;用Java编写的MySQL变更数据抓取软件。它会实时监控Mysql数据库的数据变更操作&#xff08;包括insert、update、delete&#xff09;&#xff0c;并将变更数据以 JSON 格式发送给 Kafka、Kinesi等流…

浅谈智慧路灯安全智能供电方案设计

摘要: 智慧路灯&#xff0c;作为智慧城市、新基建、城市更新的主要组成部分&#xff0c;近些年在各大城市已得到很好的落地和 应用&#xff0c;但其与传统路灯相比集成大量异元异构电子设备&#xff0c;这些设备的供电电压、接口形式、权属单位各不相同&#xff0c; 如何设计一…

《绝地求生》职业选手画面设置推荐 绝地求生画面怎么设置最好

《绝地求生》画面怎么设置最好是很多玩家心中的疑问&#xff0c;如果性能不是问题无疑高特效显示效果更好&#xff0c;但并不是所有画面参数都利于战斗&#xff0c;今天闲游盒带来分享的《绝地求生》职业选手画面设置推荐&#xff0c;赶紧来看看吧。 当前PUBG的图像设置的重要性…

YOLOv5改进 | 主干篇 | 12月份最新成果TransNeXt特征提取网络(全网首发)

一、本文介绍 本文给大家带来的改进机制是TransNeXt特征提取网络,其发表于2023年的12月份是一个最新最前沿的网络模型&#xff0c;将其应用在我们的特征提取网络来提取特征&#xff0c;同时本文给大家解决其自带的一个报错&#xff0c;通过结合聚合的像素聚焦注意力和卷积GLU&…

1131. 拯救大兵瑞恩(dp思想运用,set)

1131. 拯救大兵瑞恩 - AcWing题库 1944 年&#xff0c;特种兵麦克接到国防部的命令&#xff0c;要求立即赶赴太平洋上的一个孤岛&#xff0c;营救被敌军俘虏的大兵瑞恩。 瑞恩被关押在一个迷宫里&#xff0c;迷宫地形复杂&#xff0c;但幸好麦克得到了迷宫的地形图。 迷宫的…

MySQL(三)——函数

上期文章 MySQL&#xff08;二&#xff09;——SQL 文章目录 上期文章字符串函数数值函数日期函数流程函数总结 函数&#xff1a;一段可以直接被另一段程序调用的程序或代码 字符串函数 函数功能CONCAT(S1,S2,…Sn)字符串拼接&#xff0c;将S1,S2,…Sn拼接成一个字符串LOWER…

分布式光伏运维平台在提高光伏电站发电效率解决方案

摘要&#xff1a;伴随着能源危机和环境恶化问题的日益加重&#xff0c;科技工作者进一步加大对新能源的开发和利用。太阳能光伏发电作为新型清洁能源的主力军&#xff0c;在实际生产生活中得到了广泛的应用。然而&#xff0c;光伏发电效率偏低&#xff0c;成为制约光伏发电发展…

基于SSM的交流论坛设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue、HTML 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是…

C# wpf 实现任意控件(包括窗口)更多调整大小功能

WPF拖动改变大小系列 第一节 Grid内控件拖动调整大小 第二节 Canvas内控件拖动调整大小 第三节 窗口拖动调整大小 第四节 附加属性实现拖动调整大小 第五章 拓展更多调整大小功能&#xff08;本章&#xff09; 文章目录 WPF拖动改变大小系列前言一、添加的功能1、任意控件Drag…

tessreact训练字库

tessreact主要用于字符识别&#xff0c;除了使用软件自带的中英文识别库&#xff0c;还可以使用Tesseract OCR训练属于自己的字库。 一、软件环境搭建 使用Tesseract OCR训练自己的字库&#xff0c;需要安装Tesseract OCR和jTessBoxEditor(配套训练工具)。jTessBoxEditor需要…

ArcGIS Pro 拓扑编辑和常见一些拓扑错误处理

7.4 拓扑编辑 拓扑编辑也叫共享编辑&#xff0c;多个数据修改时&#xff0c;一块修改&#xff0c;如使用数据&#xff1a;chp7\拓扑检查.gdb,数据集DS下JZX、JZD和DK&#xff0c;加载地图框中&#xff0c;在“地图”选项卡下选择“地图拓扑”或“ds_Topology(地理数据库)”&…

自动化的自动化(1)--OPCUA2HTML5

现在的自动化工程师是令人沮丧的&#xff0c;他们努力地实现各个行业的自动化系统&#xff0c;自己却停留在敲键盘的手工劳作的阶段&#xff0c;该解放自己了。这就是“自动化实现自动化”的话题。 OPC 统一架构&#xff08;简称 OPC UA&#xff09;是现代工厂自动化中用于机器…

任务15:使用Hive进行全国气象数据分析

任务描述 知识点&#xff1a; 使用Hive进行数据分析 重 点&#xff1a; 掌握Hive基本语句熟练使用Hive对天气数据进行分析 内 容&#xff1a; 使用Hive创建外部表使用Hive对数据进行统计分析 任务指导 1. 使用Hive创建基础表 将China_stn_city.csv文件上传到HDFS的/…

广州市生物医药及高端医疗器械产业链大会暨联盟会员大会召开,天空卫士数据安全备受关注

12月20日&#xff0c;广州市生物医药及高端医疗器械产业链大会暨联盟会员大会在广州举办。在本次会议上&#xff0c;作为大会唯一受邀参加主题分享的技术供应商&#xff0c;天空卫士南区技术总监黄军发表《生物制药企业如何保护数据安全》的主题演讲。 做好承上启下“连心桥”…

概率论与数理统计————3.随机变量及其分布

一、随机变量 设E是一个随机试验&#xff0c;S为样本空间&#xff0c;样本空间的任意样本点e可以通过特定的对应法则X&#xff0c;使得每个样本点都有与之对应的数对应&#xff0c;则称XX&#xff08;e&#xff09;为随机变量 二、分布函数 分布函数&#xff1a;设X为随机变量…

使用Github + PicGo搭建个人图床,并使用CDN加速

文章目录 前言创建仓库配置PicGo如何使用 前言 在写博客的时候&#xff0c;常常需要为博客配图&#xff0c;于是一个好用稳定的图床的重要性不言而喻。本文主要介绍如何使用GitHub PicGo的方式快速搭建一个个人使用的图床。该方式方便快捷&#xff0c;还免费hh&#xff0c;唯…

git提交报错:remote: Please remove the file from history and try again.

1. 报错信息 remote: error: File: fba7046b22fd74b77425aa3e4eae0ea992d44998 500.28 MB, exceeds 100.00 MB. remote: Please remove the file from history and try again. git rev-list --objects --all | grep fba7046b22fd74b77425aa3e4eae0ea992d44998 2. 分析原因 e…

使用 Apache POI 更新/覆盖 特定的单元格

使用 Apache POI 更新特定的单元格 一. 需求二. 实现三. 效果 一. 需求 将以下表中第4行&#xff0c;第4列的单元格由“张宇”更新为“汤家凤”&#xff0c;并将更行后的结果写入新的Excel文件中&#xff1b; 二. 实现 使用Apache POI&#xff0c;可以精确定位到需要更改的单…

C#编程-自定义属性

命名自定义属性 让我们继续漏洞修复示例,在这个示例中新的自定义属性被命名为BugFixingAttribute。通常的约定是在属性名称后添加单词Attribute。编译器通过允许您调用具有短版名称的属性来支持附加。 因此,可以如以下代码段所示编写该属性: [ BugFixing ( 122,"Sara…