【MIT6.S081】Lab1: Xv6 and Unix utilities(详细解答版)

news2025/6/25 19:42:25

实验内容网址:https://xv6.dgs.zone/labs/requirements/lab1.html

Sleep

关键点:函数参数判断、系统函数调用

思路:

通过argc来判断函数参数是否正确,通过atoi函数来讲字符串转化为整型,调用sleep函数后退出程序。

代码:

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

int
main(int argc, char *argv[])
{
    int time;
    if(argc != 2){
        printf("usage:%s timexx\n",argv[0]);
        exit(-1);
    }
    time = atoi(argv[1]);
    sleep(time);
    exit(0);
}

Pingpong

关键点:

pipe()、fork()、read()、write()、getpid()

思路:

管道是作为一对文件描述符公开给进程的小型内核缓冲区,一个用于读取,一个用于写入。将数据写入管道的一端使得这些数据可以从管道的另一端读取。
其初始化方式为:

int p[2];
pipe(p);
// p[0]是管道的读取端
// P[1]是管道的写入端

根据题目要求,父进程和子进程都需要向对方发送字节,那么可以定义2个管道,代码如下

#define READ 0
#define WRITE 1
int father2child[2];
int child2father[2];
pipe(father2child);
pipe(child2father);

创建进程使用fork 函数,在父进程中,fork返回子类的PID;在子进程中,fork返回零。

步骤:

  1. 新建 pingpong.c文件,并在MakeFile文件的UPROGS中加入程序。
  2. 编写如下的代码,运行make qemu进行编译并运行pingpong.
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#define READ 0
#define WRITE 1

int
main(int argc, char *argv[])
{
    int father2child[2];
    int child2father[2];
    char sendByte = 'A';
    pipe(father2child);

    pipe(child2father);
    if(fork() == 0){
        // 子进程
        // 管道关闭,这两个方向的管道不需要使用(感觉不用也可以,可以代码写完再看看哪里需要关闭)
        close(father2child[WRITE]);
        close(child2father[READ]);
        // 子进程接收父进程的字节并打印
        char tmp;
        if(read(father2child[READ],&tmp , 1) != 1 ){
            printf("read error\n");
        }
        printf("%d: received ping\n",getpid());

        // 通过管道写入字节发送给父进程
        write(child2father[WRITE],&sendByte,1);
        // 关闭管道退出程序
        close(father2child[READ]);
        close(child2father[WRITE]);
        exit(0);

    }else{
        // 父进程
        // 管道关闭
        close(father2child[READ]);
        close(child2father[WRITE]);
        // 父进程向子进程发送一个字节
        char tmp;
        write(father2child[WRITE], &sendByte, 1);
        // 父进程读取从子进程而来的字节
        if(read(child2father[READ], &tmp, 1) != 1){
            printf("read error\n");
        }
        printf("%d: received pong\n",getpid());
        // 关闭管道退出程序
        close(father2child[WRITE]);
        close(child2father[READ]);
        exit(0);

    }

    
}

这里的close掉某一些管道的做法个人认为可以不用,当然加上去更好,避免错误使用到这些管道端口。对于2对管道4个管道端口,父子进程都有这4个管道端口的文件描述符,对于父进程,可以把father2child[READ]和child2father[WRITE]关闭掉,这不会影响子进程对这两个端口的使用。对于子进程则把相反的管道端口关闭。

Primes

关键点:

pipe() 、fork()、递归思想、素数判断

思路:

一开始看的时候会看不懂题目,题目的要求就是找出2-35中的素数(质数)。本题寻找素数的方法可以看下图
在这里插入图片描述
对于一个进程,从左管道中读入第一个数,然后将第一个数打印出来(这个数是素数)。然后从左管道读入其他数,将其他数与第一个数相除,若不整除,则将该数写入右管道中。递归后可以得到2-35中的每一个素数。
本题涉及到递归思想,需要构造递归函数;然后是对于每个进程都需要有2个管道,一个管道是父进程与子进程的,一个管道是子进程与孙进程的;最后还需要及时关闭不使用的管道端口,避免系统资源不够用。

步骤:

  1. 从第一个进程(戏称:始祖进程hhh)开始,定义管道,遍历2-35,将每个整型写入管道中,调用fork生成子进程,父进程则关闭管道的文件描述符并退出。
  2. 从第二个进程开始,需要使用递归了,则编写递归函数 primes(int* left_pipe) ,该函数的参数是父进程给子进程的左管道。在primes函数中,读取左管道第一个数作为素数并打印出来。接着创建新的管道(可以认为是右管道)。然后从左管道中读取每个整型,经过是否素数判断,若是则写入右管道中;接着进行fork创建孙进程,孙进程中继续调用primes(right_pipe),将右管道传给孙进程。子进程则关闭不使用的文件描述符,退出进程。
  3. 递归…直到最后一个进程连第一个数值也读不到的时候直接退出进程,程序结束。

代码:

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#define READ 0
#define WRITE 1

void primes(int* left_pipe){
    // 关闭左管道的write端口
    close(left_pipe[WRITE]);
    // 读取第一个值
    int first = 0;
    if(read(left_pipe[READ],&first,sizeof(int)) != sizeof(int)){
        exit(0);
    }
    // 按照要求打印
    printf("primes %d\n",first);
    // 创建管道
    int right_pipe[2];
    pipe(right_pipe);
    // 从左管道读入并写到右管道中
    int num;
    while(read(left_pipe[READ], &num,sizeof(int) == sizeof(int))){
        if(num%first != 0){
            // 将素数写入右管道中
            write(right_pipe[WRITE],&num, sizeof(int));
        }
    }
    // 创建子进程
    if(fork() == 0){
        primes(right_pipe);
    }
    else{
        close(left_pipe[READ]);
        close(right_pipe[READ]);
        close(right_pipe[WRITE]);
        // 等待子进程退出?
        wait(0);
    }
    exit(0);
}

int
main(int argc, char *argv[])
{
    // 定义一个管道
    int p[2];
    pipe(p);
    //从第一个进程开始,将2-35输入到子进程中
    for(int i = 2; i <= 35; i++){
        write(p[WRITE],&i,sizeof(int));
    }
    if(fork() == 0){
        // 子进程
        primes(p);
    }
    else{
        close(p[WRITE]);
        close(p[READ]);
        // 等待子进程退出?
        wait(0);
    }
    

    exit(0);
}

未解:

父进程使用wait(0)会等待子进程全部退出后才退出吗?

Find

关键点:user/ls.c实现、目录遍历、递归思想

思路:

首先要弄懂 user/ls.c中功能的实现。
fmtname()的作用是去除文件路径中的路径,只保留住文件名,如果文件名程度超过DIRSIZ则直接返回,若不超过则要补全空格。
如果path 是一个文件的话,则直接输出文件名。若path是一个目录的话。循环读取目录中的每个条目,并检查其索引节点号 (inum)。如果索引节点号为0,我们跳过该条目。否则,我们将条目的名称复制到 buf 的末尾,并添加一个终止字符。接着,我们获取该条目的状态信息,并打印其信息。如果获取状态信息失败,我们打印错误消息并继续下一个条目。
搞懂user/ls.c后,就可以在这上面做修改。

步骤:

  1. 新建find.c文件,首先进行参数检查
  2. 定义递归函数,传入本轮递归的路径和目标文件名。
  3. 递归函数中,如果path是文件名的话则进行文件名判断,再打印。如果是目录,则循环遍历每一个条目,过滤掉. …后进行递归。具体还是看代码吧,思路比较清晰。

代码:

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

// 用于分割出带有/路径的文件名
// input:a/b/filename
// output:filename
char*
my_fmtname(char *path)
{
  char *p;
  // Find first character after last slash.
  for(p=path+strlen(path); p >= path && *p != '/'; p--)
    ;
  p++;

  return p;
}

void find(char* path, char* target_filename){
    // 模仿ls函数
    char buf[512], *p;  // 用于存储文件或目录路径的缓冲区。
    int fd;             // 文件描述符,用于后续的文件或目录操作。
    struct dirent de;   // 一个 dirent 结构体,用于读取目录中的条目。
    struct stat st;     // 一个 stat 结构体,用于存储文件或目录的状态信息。
    // 尝试打开目录
    if((fd = open(path, 0)) < 0){
        fprintf(2, "find: cannot open %s\n", path);
        return;
    }
    // 尝试获取状态信息
    if(fstat(fd, &st) < 0){
        fprintf(2, "find: cannot stat %s\n", path);
        close(fd);
        return;
    }

    switch(st.type){
    // 如果path指向的是一个文件,判断该文件与目标文件名是否一样
    case T_FILE:
       
       if(strcmp(my_fmtname(path),target_filename) == 0){
        // 表示文件名一样
        printf("%s\n",path);
       }
       break;
    case T_DIR:
        if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){
            printf("find: path too long\n");
            break;
        }
        strcpy(buf, path);
        p = buf+strlen(buf);
        *p++ = '/';
        // 循环读取目录中的每个条目,并检查其索引节点号
        while(read(fd, &de, sizeof(de)) == sizeof(de)){
            if(de.inum == 0)
                continue;
            // 过滤 . ..
            if(!strcmp(de.name,".") || !strcmp(de.name,".."))
                continue;
            memmove(p, de.name, DIRSIZ);
            p[DIRSIZ] = 0;
            if(stat(buf, &st) < 0){
                printf("find: cannot stat %s\n", buf);
                continue;
            }
            //递归!!!
            find(buf, target_filename);
        }
        break;
    }
    close(fd);
}

void
main(int argc, char *argv[])
{
  
    if(argc != 3){
        printf("usage: %s <dir> <filename>",argv[0]);
        exit(0);
    }
    find(argv[1], argv[2]);
    exit(0);
}

Xargs

待施工

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

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

相关文章

Harmony鸿蒙南向驱动开发-MIPI DSI

功能简介 DSI&#xff08;Display Serial Interface&#xff09;是由移动行业处理器接口联盟&#xff08;Mobile Industry Processor Interface (MIPI) Alliance&#xff09;制定的规范&#xff0c;旨在降低移动设备中显示控制器的成本。它以串行的方式发送像素数据或指令给外…

5.2 配置静态路由

5.2.1 实验1&#xff1a;配置IPv4静态路由 1、实验目的 通过本实验可以掌握&#xff1a; 配置带下一跳地址的IPv4静态路由的方法。配置带送出接口的IPv4静态路由的方法。配置总结IPv4静态路由的方法。配置浮动IPv4静态路由的方法。代理 ARP的作用。路由表的含义。扩展ping命…

铸造大型基础平板的结构应该怎样设计

设计大型基础平板的结构时&#xff0c;需要考虑以下几个方面&#xff1a; 地质条件&#xff1a;首先要了解工程所在地的地质条件&#xff0c;包括土质、地下水位、地震状况等。根据地质条件来选择合适的基础类型&#xff0c;如浅基、深基或地下连续墙等。 荷载分析&#xff1a…

使用docker制作Android镜像(实操可用)

一、安装包准备 1、准备jdk 下载地址&#xff1a;Java Downloads | Oracle 注意版本&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 我下载的jdk17&#xff0c;不然后面构建镜像报错&#xff0c;就是版本不对 2、准备安装的工具包 ttps://dev…

大话设计模式之迭代器模式

迭代器模式是一种行为设计模式&#xff0c;它允许客户端逐个访问集合中的元素&#xff0c;而不暴露集合的底层表示。这种模式提供了一种方法来访问聚合对象中的各个元素&#xff0c;而不需要暴露其内部结构。 迭代器模式由以下几个关键角色组成&#xff1a; 迭代器&#xff08…

【产品】ANET智能通信管理机 物联网网关 电力监控/能耗监测/能源管理系统

产品概述 本系列智能通信管理机是一款采用嵌入式硬件计算机平台&#xff0c;具有多个下行通信接口及一个或者多个上行网络接口&#xff0c;用于将一个目标区域内所有的智能监控/保护装置的通信数据整理汇总后&#xff0c;实时上传主站系统&#xff0c;完成遥信、遥测等能源数据…

js爬虫puppeteer库 解决网页动态渲染无法爬取

我们爬取这个网址上面的股票实时部分宇通客车(600066)_股票价格_行情_走势图—东方财富网 我们用正常的方法爬取会发现爬取不下来&#xff0c;是因为这个网页这里是实时渲染的&#xff0c;我们直接通过网址接口访问这里还没有渲染出来 于是我们可以通过下面的代码来进行爬取: …

6.1Python之字典的初识

【1】字典的创建与价值 字典&#xff08;Dictionary&#xff09;是一种在Python中用于存储和组织数据的数据结构。元素由键和对应的值组成。其中&#xff0c;键&#xff08;Key&#xff09;必须是唯一的&#xff0c;而值&#xff08;Value&#xff09;则可以是任意类型的数据。…

springboot+vue全栈开发【1.准备工作篇】

目录 前言环境配置关于Maven 前言 为了坚持每天写博客所以开了这个系列~~ 环境配置 1.配置java环境&#xff0c;下载jdk&#xff0c;配置环境变量 这一步有很多教程&#xff0c;不赘述了 2.安装idea 关于Maven Maven是一个项目管理工具&#xff0c;可以对java项目进行自动化…

Vue实现防篡改水印的效果。删除元素无效!更改元素属性无效!支持图片、元素、视频等等。

1、演示 2、水印的目的 版权保护&#xff1a;水印可以在图片、文档或视频中嵌入作者、品牌或版权所有者的信息&#xff0c;以防止未经授权的复制、传播或使用。当其他人使用带有水印的内容时&#xff0c;可以追溯到原始作者或版权所有者&#xff0c;从而加强版权保护。 身份识…

AI日报:GPT-4-Turbo正式版自带读图能力;Gemini1.5Pro开放API;SD3将于4月中旬发布;抖音宫崎骏AI特效爆火

欢迎来到【AI日报】栏目!这里是你每天探索人工智能世界的指南&#xff0c;每天我们为你呈现AI领域的热点内容&#xff0c;聚焦开发者&#xff0c;助你洞悉技术趋势、了解创新AI产品应用。 新鲜AI产品点击了解&#xff1a;AIbase - 智能匹配最适合您的AI产品和网站 &#x1f4f…

特征融合篇 | YOLOv8改进之将Neck网络更换为多级特征融合金字塔HS-FPN | 助力小目标检测

前言:Hello大家好,我是小哥谈。HS-FPN(Hierarchical Scale Feature Pyramid Network)是一种用于目标检测任务的网络结构。它是在传统的Feature Pyramid Network(FPN)基础上进行改进的。HS-FPN的主要目标是解决目标检测中存在的多尺度问题。在传统的FPN中,通过在不同层级…

CNN-Transformer时间序列预测

部分代码&#xff1a; # CNN-Transformer class CNNTransformerEncoder(nn.Module):def __init__(self, input_features, transformer_encoder_heads,embedding_features, cnn_kernel_size, dim_feedforward_enc, n_encoder_layer):super(CNNTransformerEncoder, self).__init…

大语言模型:红蓝对抗的工作原理及作用

您是否对强大的生成式AI领域心生好奇&#xff0c;却又担心随之而来的潜在漏洞&#xff1f;您只需了解红蓝对抗就好了&#xff0c;它也称为破解或提示注入。AI开发的这一关键环节往往被忽视&#xff0c;但其在提高生成式AI模型的性能方面发挥的作用却至关重要。 大语言模型&…

JavaScript进阶6之函数式编程与ES6ESNext规范

函数式编程 柯里化currycurrycompose示例&#xff1a;简化版展开写&#xff1a; debug示例一&#xff1a;示例二&#xff1a; 模板字符串css in js方案 箭头函数问题 生成器 generator应用场景 反射 Reflect 柯里化curry compose是curry的应用 在 lodash/fp underscore ramba …

vue2创建项目的两种方式,配置路由vue-router,引入element-ui

提示&#xff1a;vue2依赖node版本8.0以上 文章目录 前言一、创建项目基于vue-cli二、创建项目基于vue/cli三、对吧两种创建方式四、安装Element ui并引入五、配置路由跳转四、效果五、参考文档总结 前言 使用vue/cli脚手架vue create创建 使用vue-cli脚手架vue init webpack创…

JVM虚拟机(二)类加载器、双亲委派模型、类装载的执行过程

目录 一、类加载器1.1 什么是类加载器&#xff1f;1.2 类加载器的分类 二、双亲委派模型2.1 什么是双亲委派模型&#xff1f;1&#xff09; 示例一&#xff1a;加载自己创建的类2&#xff09;示例二&#xff1a;加载JDK原有的类 2.2 JVM 为什么采用双亲委派模型&#xff1f; 三…

3. DAX 时间函数-- DATE 日期--一生二,二生三,三生万物

在数据分析过程中&#xff0c;经常需要从一个数据推到另外一个数据&#xff0c;日期数据也是如此&#xff0c;需要从一个日期推到另外一个相关的日期&#xff0c;或者从一群日期推到另外一个相关的日期/一群相关的日期。这一期说的就是日期之间彼此推衍的函数&#xff0c;会比之…

笔记83:二叉树前中后序遍历(迭代法 + 栈)

题目&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 注意&#xff1a;以下代码均为个人尝试编写&#xff0c;并非力扣题解&#xff0c;因此时间和空间复杂度可能并不是最优的&#xff0c;只是记录一下自己当时写这个题的时候的思路&#xff1b; 前序遍历&#xff1a; …

嵌入式ARM版本银河麒麟操作系统V10SP1安装OPenGauss数据库

前言&#xff1a; 官网提供了非常完整的openGauss安装步骤。 https://opengauss.org/zh/download/archive/列举一下个人的使用环境&#xff1a; 麒麟V10 rk3588工控板&#xff08;ARM&#xff09; openGauss-3.0.5&#xff08;极简版&#xff09;浏览一下官网&#xff0c;可以…