MIT 6s081 lab 1.Xv6 and Unix utilities

news2024/11/18 11:18:28

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/1386202.html

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

相关文章

coala,一个超级实用的 Python 库!

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 大家好&#xff0c;今天为大家分享一个超级实用的 Python 库 - coala。 Github地址&#xff1a;https://github.com/coala/coala/ 在现代软件开发中&#xff0c;代码质量和一致性是非常重要的。然而&#xff0c…

GPT有什么用?对哪些行业帮助比较大?无际Ai带来介绍

GPT 是“Generative Pre-trained Transformer”的缩写。这是一种由 OpenAI 开发的人工智能语言模型&#xff0c;它采用了变压器&#xff08;Transformer&#xff09;架构&#xff0c;并且在大规模文本数据上进行了预训练。GPT 系列模型的目标是生成具有高度连贯性和语义合理性的…

跨国企业如何高效又安全的传输视频大文件?

在视频传输需求日益增长的今天&#xff0c;如何高效、安全地传输视频大文件成为跨国企业面临的重要问题。传统的文件传输方式存在诸多弊端&#xff0c;无法满足跨国企业对于传输效率、文件安全以及合规性的需求。那么跨国企业如何在市场是找到一种文件传输工具能在安全性、稳定…

两周掌握Vue3(三):全局组件、局部组件、Props

文章目录 一、全局组件1.创建全局组件2.在main.js中注册全局组件3.使用全局组件 二、局部组件1.创建局部组件2.在另一个组件中注册、使用局部组件 三、Props1.定义一个子组件2.定义一个父组件3.效果 代码仓库&#xff1a;跳转 本博客对应分支&#xff1a;03 一、全局组件 Vue…

map和set使用讲解

map的使用 map的介绍 map是C STL&#xff08;标准模板库&#xff09;中的一个关联容器。 它提供了一种有序的键值对存储方式&#xff0c;其中每个元素都由一个键和一个值组成。 map中的元素根据键的值自动进行排序&#xff0c;并且通过键快速访问对应的值。 map使用红黑树数…

【HTML5】 canvas 绘制图形

文章目录 一、基本用法二、用法详见2.0、方法属性2.1、绘制线条2.2、绘制矩形2.3、绘制圆形2.4、绘制文本2.5、填充图像 一、基本用法 canvas 标签&#xff1a;可用于在网页上绘制图形&#xff08;使用 JavaScript 在网页上绘制图像&#xff09;画布是一个矩形区域&#xff0c…

如何搭建企业管理系统Odoo并实现无公网ip远程访问管理界面

文章目录 前言1. 下载安装Odoo&#xff1a;2. 实现公网访问Odoo本地系统&#xff1a;3. 固定域名访问Odoo本地系统 前言 Odoo是全球流行的开源企业管理套件&#xff0c;是一个一站式全功能ERP及电商平台。 开源性质&#xff1a;Odoo是一个开源的ERP软件&#xff0c;这意味着企…

软件设计不是CRUD(10):低耦合模块设计理论——业务抽象:从需求中提取业务维度

接上文《软件设计不是CRUD(9):低耦合模块设计理论——设计落地所面临的挑战》 2、什么是业务抽象 业务抽象是一种将需求落地成模块功能的设计思想,是对业务需求和技术设计进行转换、隔离的一种分析方法。经过业务抽象后的业务模块一般具有较高的业务屈服度,能更大程度满…

Nginx设置域名转发到服务器指定的端口

&#x1f341; 作者&#xff1a;知识浅谈&#xff0c;CSDN签约讲师&#xff0c;CSDN博客专家&#xff0c;华为云云享专家&#xff0c;阿里云专家博主 &#x1f4cc; 擅长领域&#xff1a;全栈工程师、爬虫、ACM算法 &#x1f492; 公众号&#xff1a;知识浅谈 &#x1f525;网站…

git提交文本或者word到git教程,git创建仓库时候自带

简易的命令行入门教程: Git 全局设置: git config --global user.name “XX” git config --global user.email “XXXqq.com” 创建 git 仓库: mkdir test cd test git init touch README.md git add README.md git commit -m “first commit” git remote add origin https:…

一款全面且多功能的安全扫描仪

工具介绍 Trivy&#xff08;发音&#xff09;是一款全面且多功能的安全扫描仪。Trivy 拥有用于查找安全问题的扫描仪&#xff0c;以及可以找到这些问题的目标。 目标&#xff08;Trivy 可以扫描的内容&#xff09;&#xff1a; 容器镜像 文件系统 Git 存储库&#xff08;远…

基础篇_开发web程序(C/S架构,SpringBoot,贷款计算器-WEB版)

文章目录 一. C/S 架构1. C/S 架构2. URL 格式 二. Spring Boot1. 向导生成2. 准备工作1) 修改版本2) 修改maven 设置 3. 导入模块4. hello world5. 处理输入页面接收参数练习 - 加法 三. 贷款计算器 - WEB 版1. 数组定义改写贷款计算器越界遍历默认值 2. 二维数组3. 贷款计算器…

基于ssm的疫情防控管理系统设计与实现论文

摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装疫情防控管理系统软件来发挥其高效地信息处理的作用&#x…

感知器学习算法和Adaline规则

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

【Spring】SpringBoot 统一功能处理

文章目录 前言1. 拦截器1.1 什么是拦截器1.2 拦截器的使用1.2.1 自定义拦截器1.2.2 注册配置拦截器 1.3 拦截器详解1.3.1 拦截路径1.3.2 拦截器执行流程1.3.3 适配器模式 2. 统一数据返回格式3. 统一异常处理 前言 在日常使用 Spring 框架进行开发的时候&#xff0c;对于一些板…

flutter给组件设置背景图的操作

可以设置背景图的组件只有一个&#xff0c;那就是Container容器&#xff0c;要想设置背景图&#xff0c;可以使用网路图片&#xff0c;也可以使用本地图片&#xff0c;要是使用本地图片&#xff0c;需要在本地添加一个资源路径&#xff0c;用来管理这些文件&#xff0c;在本地项…

若依框架实现排序【升序或降序】很简单

前端实现 1. 在表格上加监听函数sort-change。如下红框所示&#xff1a; 2. 在表行上加排序字:sort-orders&#xff0c;可排序字sortable。如下红框所示&#xff1a; 3. 添加监听函数实现。代码如下&#xff1a; handleSortChange(column) {this.queryParams.orderByColumn …

【深入理解 ByteBuf 之三 接口类拆解】2. Recycler 接口设计真正的回收机制

Recycler 回收器接口设计 本节接着 ObjectPool 的设计脉络&#xff0c;具体看看其具体实现 RecyclerObjectPool 中引用的 Recycler 究竟是怎么实现的 这一张图基本已经说明白了&#xff0c;我再做个总结&#xff0c;对细节感兴趣的可以看看我下面带源码的注释。 对于 Recycle…

debian 11 arm64 aarch64 源码变异winehq arm64 笔记

安装华为毕昇编译器 sudo apt install libc1-13 编译tools cd tools su root export PATH/opt/bisheng-compiler-1.3.3-aarch64-linux/bin:$PATH rootdebian:/home/yeqiang/下载/src/wine/tools# ../configure CC/opt/bisheng-compiler-1.3.3-aarch64-linux/bin/clang C…

职场硬货:刚入职面对陌生的被测系统, 没有需求文档如何快速熟悉?

各位小伙伴大家好, 今天为大家分析一下在企业中, 如果快速的上手被测系统/软件, 了解产品目标业务需求, 做到尽快尽职完成测试工作。 找到所有可能的相关文档 尽可能找到项目开发计划书, 项目签订的合同, 一般合同中会包含项目研发背景, 与产品及功能点概述, 这样可以先了解项…