128.Mit6.S081-实验1-Xv6 and Unix utilities(下)

news2024/11/19 11:22:41

今天我们继续实验一接下来的内容。

一、pingpong(难度easy)

1.需求

        编写一个程序,使用 UNIX 系统调用通过一对管道(每个方向一个管道)在两个进程之间 "ping-pong" 传递一个字节。父进程应该向子进程发送一个字节; 子进程应该打印<pid>: received ping,其中 <pid> 是它的进程号,将管道上的字节写入父进程,然后退出; 父进程应该从子进程读取字节,打印<pid>: received pong,然后退出。您的解决方案应该在user/pingpong.c.文件中。

2.提示

  • 使用pipe来创造管道

  • 使用fork创建子进程

  • 使用read从管道中读取数据,并且使用write向管道中写入数据

  • 使用getpid获取调用进程的pid

  • 将程序加入到MakefileUPROGS

  • xv6上的用户程序有一组有限的可用库函数。您可以在user/user.h中看到可调用的程序列表;源代码(系统调用除外)位于user/ulib.cuser/printf.cuser/umalloc.c中。

3.代码演示

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

int main(int argc, char *argv[])
{
    char buff; // 用于传送的字节

    int parent_to_child[2]; // 父进程到子进程的管道
    int child_to_parent[2]; // 子进程到父进程的管道

    pipe(parent_to_child);
    pipe(child_to_parent);

    int pid = fork();
    if (pid < 0)
    {
        fprintf(2, "fork failed!");
        exit(1);
    }

    if (pid == 0)
    {
        // 子进程
        close(parent_to_child[1]); // 关闭写端
        close(child_to_parent[0]); // 关闭读端

        read(parent_to_child[0], &buff, 1); // 从父进程读取进程
        printf("%d: received ping\n", getpid());

        write(child_to_parent[1], &buff, 1); // 向父进程写入字节
        exit(0);
    }
    else
    {
        // 父进程
        close(parent_to_child[0]); // 关闭读端
        close(child_to_parent[1]); // 关闭写端

        buff = 'p';
        write(parent_to_child[1], &buff, 1); // 向父进程写入字节

        read(child_to_parent[0], &buff, 1); // 从子进程读取字节
        printf("%d: received pong\n", getpid());

        exit(0);
    }

    exit(0);
}

4.测试结果

在xv6-labs-2020中,执行下面指令,测试程序

5.辅助图

二、primes实验(素数,难度Hard)

1.需求

        您的目标是使用pipefork来设置管道。第一个进程将数字2到35输入管道。对于每个素数,您将安排创建一个进程,该进程通过一个管道从其左邻居读取数据,并通过另一个管道向其右邻居写入数据。由于xv6的文件描述符和进程数量有限,因此第一个进程可以在35处停止。您的解决方案应该在user/primes.c.文件中。

2.提示

  • 使用pipe来创造管道

  • 使用fork创建子进程

  • 使用read从管道中读取数据,并且使用write向管道中写入数据

  • 使用getpid获取调用进程的pid

  • 将程序加入到MakefileUPROGS

3.代码演示

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

void get_primes(int *input, int num)
{
    if (num == 1)
    {
        printf("prime %d\n", *input);
        return;
    }

    int p[2], i;
    int prime = *input;
    int temp;
    printf("prime %d\n", prime);
    pipe(p);
    if (fork() == 0)
    {
        for (i = 0; i < num; i++)
        {
            temp = *(input + i);
            write(p[1], (char *)(&temp), 4);
        }
        exit(0);
    }
    close(p[1]);
    if (fork() == 0)
    {
        int counter = 0;
        char buffer[4];
        while (read(p[0], buffer, 4) != 0)
        {
            temp = *((int *)buffer);
            if (temp % prime != 0)
            {
                *input = temp;
                input += 1;
                counter++;
            }
        }
        get_primes(input - counter, counter);
        exit(0);
    }
    wait(0);
    wait(0);
}

int main()
{
    int input[34];
    int i = 0;
    for (; i < 34; i++)
    {
        input[i] = i + 2;
    }
    get_primes(input, 34);
    exit(0);
}

这段代码是用C语言编写的,它的目的是找出2到35之间的所有素数(不包括2,因为2是第一个素数)。代码使用了进程间的管道通信来实现这一目标。下面是代码的详细解释:

  1. get_primes函数:这个函数接收一个整型数组input和一个整型变量num作为参数。input数组存储了一系列待检查的整数,num表示数组中整数的数量。

  2. get_primes函数中,首先检查num是否等于1。如果是,那么input数组中只剩下一个整数,这个整数就是素数,将其打印出来并返回。

  3. 如果num大于1,首先创建一个管道p。管道用于在父子进程之间传递数据。

  4. 使用fork()创建一个子进程。在子进程中,遍历input数组,将每个元素写入管道p的写入端。

  5. 在父进程中,再次调用fork()创建另一个子进程。在这个子进程中,从管道p的读取端读取数据,检查每个读取到的整数是否可以被prime整除。如果不能,说明它是素数,将其存储回input数组,并更新计数器counter

  6. 当子进程读取完所有数据后,递归调用get_primes函数,将剩余的素数继续传递给下一个子进程。

  7. main函数中,首先初始化一个长度为34的input数组,存储2到35之间的所有整数。然后调用get_primes函数,开始查找素数。

  8. 最后,程序退出。

4.测试结果

三、find实验(难度Moderate)

1.需求

        写一个简化版本的UNIX的find程序:查找目录树中具有特定名称的所有文件,你的解决方案应该放在user/find.c。

2.提示

  • 查看user/ls.c文件学习如何读取目录
  • 使用递归允许find下降到子目录中
  • 不要在“.”和“..”目录中递归
  • 对文件系统的更改会在qemu的运行过程中一直保持;要获得一个干净的文件系统,请运行make clean,然后make qemu
  • 你将会使用到C语言的字符串,要学习它请看《C程序设计语言》(K&R),例如第5.5节
  • 注意在C语言中不能像python一样使用“==”对字符串进行比较,而应当使用strcmp()
  • 将程序加入到Makefile的UPROGS

3.代码演示

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

char *get_fname(char *path) // 获取当前文件名
{
    char *p;
    for (p = path + strlen(path); p >= path && *p != '/'; p--)
        ;
    p++;
    return p;
}

void find(char *path, char *str) // 类Unix系统中,目录被视为一种特殊类型的文件
{
    char buff[512];         // 存储路径
    struct dirent de;       // 目录结构体
    struct stat st;         // 文件结构体
    int fd = open(path, 0); // 0表示以标准模式(读写模式)打开
    if (fd < 0)
    {
        fprintf(2, "find: cannot open %s\n", path);
        return;
    }
    if (fstat(fd, &st) < 0) // 通过文件描述符将对应的文件信息放入文件结构体stat中,若失败则返回-1
    {
        fprintf(2, "find: cannot stat %s\n", path);
        close(fd);
        return;
    }
    switch (st.type) // 判断打开类型
    {
    case T_DEVICE: // 判断为设备文件
    case T_FILE:   // 判断为普通文件
        if (!strcmp(str, get_fname(path)))
        {
            printf("%s\n", path);
        }
        break;
    case T_DIR: // 判定为目录
        if (strlen(path) + 1 + DIRSIZ + 1 > sizeof buff)
        {
            printf("find: path too long\n");
            break;
        }
        strcpy(buff, path);
        char *p = buff + strlen(buff);
        *p = '/';
        p++;
        while (read(fd, &de, sizeof(de)) == sizeof(de)) // 使用read从目录文件中读取目录条目,处理目录中文件
        {
            if (de.inum == 0) // 该目录条目为空或未使用
                continue;
            memmove(p, de.name, DIRSIZ);
            p[DIRSIZ] = 0;
            if (stat(buff, &st) < 0)
            {
                printf("find: cannot stat %s\n", buff);
                continue;
            }
            if (st.type == T_DEVICE || st.type == T_FILE)
            { // 判断为非目录文件
                if (!strcmp(str, get_fname(buff)))
                    printf("%s\n", buff);
            }
            else if (st.type == T_DIR && strcmp(".", get_fname(buff)) && strcmp("..", get_fname(buff))) // 判定为子目录,递归处理,注意不要重复进入本目录以及父目录
                find(buff, str);
        }
        break;
    }
    close(fd);
    return;
}

int main(int argc, char *argv[])
{
    if (argc == 3)
        find(argv[1], argv[2]);
    else
        printf("argument error\n");
    exit(0);
}

这段代码是一个简单的类Unix系统下的文件查找程序,它接受两个命令行参数:一个路径和一个字符串。程序会递归地在指定路径下查找与给定字符串匹配的文件名,并打印出匹配文件的完整路径。

以下是代码的主要部分的解释:

  1. get_fname函数:接受一个路径字符串,返回该路径中最后一个斜杠('/')之后的文件名部分。如果路径中没有斜杠,则返回整个路径。

  2. find函数:接受一个路径和一个字符串,用于递归地在路径中查找与字符串匹配的文件名。

    • 打开路径,使用open函数。
    • 使用fstat函数获取文件状态,判断文件类型。
    • 如果是文件或设备文件,并且文件名与字符串匹配,打印路径。
    • 如果是目录,递归地调用find函数。
  3. main函数:检查命令行参数的数量,如果正确,调用find函数开始查找。

这个程序的工作原理是,对于每个目录,它都会读取目录中的所有条目,忽略...这两个特殊条目,然后对于每个条目,它会检查是否是文件或目录。如果是文件,它会检查文件名是否与给定的字符串匹配;如果是目录,它会递归地调用find函数。

4.测试结果

四、xargs(难度Moderate)

1.需求

        编写一个简化版UNIX的xargs程序:它从标准输入中按行读取,并且为每一行执行一个命令,将行作为参数提供给命令。你的解决方案应该在user/xargs.c

2.提示

提示:

  • 使用forkexec对每行输入调用命令,在父进程中使用wait等待子进程完成命令。
  • 要读取单个输入行,请一次读取一个字符,直到出现换行符('\n')。
  • kernel/param.h声明MAXARG,如果需要声明argv数组,这可能很有用。
  • 将程序添加到Makefile中的UPROGS
  • 对文件系统的更改会在qemu的运行过程中保持不变;要获得一个干净的文件系统,请运行make clean,然后make qemu

3.代码演示

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

int main(int argc, char *argv[])
{
    char *p[MAXARG];
    int i;

    if (argc < 2)
    {
        fprintf(2, "Usage: %s <command> [args...]\n", argv[0]);
        exit(1);
    }

    for (i = 1; i < argc; i++)
    {
        p[i - 1] = argv[i];
    }

    char buffer[512]; // 缓冲区用于存储从标准输入读取的行

    while (gets(buffer, sizeof(buffer)))
    {
        if (buffer[0] == 0) // 空行跳过
            continue;

        // 去掉行末的换行符
        if (buffer[strlen(buffer) - 1] == '\n')
            buffer[strlen(buffer) - 1] = 0;

        // 将输入行添加到参数数组
        p[argc - 1] = buffer;
        p[argc] = 0; // 确保参数数组以 NULL 结尾

        int pid = fork();
        if (pid < 0)
        {
            fprintf(2, "fork failed\n");
            exit(1);
        }
        else if (pid == 0)
        {
            exec(argv[1], p);
            // exec 如果成功,下面的代码不会执行
            fprintf(2, "exec %s failed\n", argv[1]);
            exit(1);
        }
        else
        {
            wait(0); // 父进程等待子进程结束
        }
    }

    exit(0);
}

这段代码是一个简单的Unix-like shell程序,它接受命令行参数中的第一个参数作为要执行的命令,然后从标准输入读取每一行,将每一行作为参数传递给该命令,并执行它。

以下是代码的主要部分的解释:

  1. main函数首先检查命令行参数的数量,如果小于2,则打印使用说明并退出。

  2. 然后将命令行参数(除了第一个参数,即程序名称本身)复制到一个新的参数数组p中。

  3. 使用gets函数从标准输入读取一行,存储在buffer中。如果读取的是空行,则跳过。

  4. 去掉行末的换行符,并将读取的行添加到参数数组p的末尾,确保数组以NULL结尾,这是exec函数要求的。

  5. 使用fork函数创建一个新的进程。如果fork失败,则打印错误信息并退出。

  6. 在子进程中,使用exec函数执行命令行参数中指定的命令,并传递参数数组p。如果exec失败,则打印错误信息并退出。

  7. 在父进程中,使用wait函数等待子进程结束。

  8. 重复步骤3-7,直到标准输入结束。

  9. 程序退出。

4.测试结果

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

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

相关文章

短视频语音合成:成都鼎茂宏升文化传媒公司

短视频语音合成&#xff1a;技术革新与创意融合的新篇章 随着科技的飞速发展&#xff0c;短视频已经成为人们生活中不可或缺的一部分。在这个快速变化的时代&#xff0c;短视频语音合成技术正逐渐崭露头角&#xff0c;以其独特的魅力和广泛的应用前景&#xff0c;吸引了众多创…

R语言:ROC分析

> install.packages("pROC") > library(pROC) > inputFile"结果.txt" > rtread.table(inputFile, headerT, sep"\t", check.namesF, row.names1) > head(rt) con treat TCGA-E2-A1L7-11A-con…

6. 第K小的和-二分

6.第K小的和 - 蓝桥云课 (lanqiao.cn) #include <bits/stdc.h> #define int long long #define endl \n using namespace std; int n,m,k,an[100005],bm[100005]; int check(int x){int res0;//序列C中<x的数的个数for(int i0;i<n;i){//遍历数组A&#xff0c;对于每…

怎么得到所有大写字母/小写字母组成的字符串

有时候&#xff0c;可能需要获取a~z、A~Z组成的26个字母的字符串&#xff0c;这篇文章介绍一种简单的方法。 只需要几句简单到不能再简单的代码&#xff01;你不会还在傻傻地一个个字母敲吧~ /*** author heyunlin* version 1.0*/ public class Example {/*** 小写字母*/priv…

Vscode编辑器 js 输入log自动补全

最近换了新电脑&#xff0c;新下载了Vscode&#xff0c;记录一下设置项。 Vscode 版本 想要的效果 js文件中输入log&#xff08;点击tab键&#xff09;&#xff0c;自动补全为 console.log() Vscode 文件》首选项》设置 搜索&#xff1a;snippets Emmet: Show Suggestions…

HTML常用标签-表单标签

表单标签 1 表单标签2 表单项标签2.1 单行文本框2.2 密码框2.3 单选框2.4 复选框2.5 下拉框2.6 按钮2.7 隐藏域2.8 多行文本框2.9 文件标签 1 表单标签 表单标签,可以实现让用户在界面上输入各种信息并提交的一种标签. 是向服务端发送数据主要的方式之一 form标签,表单标签,其内…

3ds Max与Maya不同之处?两者哪个更适合云渲染?

3ds Max 和 Maya 都是知名的3D软件&#xff0c;各有其特色。3ds Max 以直观的建模和丰富的插件生态闻名&#xff1b;Maya 则在动画和角色创作方面更为出色。两者都支持云渲染技术&#xff0c;能帮助用户在云端高效完成项目。 一、3ds Max和Maya之间的主要区别&#xff1a; 3ds…

web入门练手案例(二)

下面是一下web入门案例和实现的代码&#xff0c;带有部分注释&#xff0c;倘若代码中有任何问题或疑问&#xff0c;欢迎留言交流~ 数字变色Logo 案例描述 “Logo”是“商标”的英文说法&#xff0c;是企业最基本的视觉识别形象&#xff0c;通过商标的推广可以让消费者了解企…

两小时看完花书(深度学习入门篇)

1.深度学习花书前言 机器学习早期的时候十分依赖于已有的知识库和人为的逻辑规则&#xff0c;需要人们花大量的时间去制定合理的逻辑判定&#xff0c;可以说是有多少人工&#xff0c;就有多少智能。后来逐渐发展出一些简单的机器学习方法例如logistic regression、naive bayes等…

产品品牌CRUD

文章目录 1.renren-generator生成CRUD1.数据库表设计1.数据表设计2.分析 2.代码生成器生成crud1.查看generator.properties&#xff08;不需要修改&#xff09;2.修改application.yml 连接的数据库修改为云数据库3.启动renren-generator模块4.浏览器访问 http://localhost:81/5…

ip addr 或 ip address 是 Linux 系统中的一个命令,用于显示或修改网络接口的地址信息。

ip addr 或 ip address 是 Linux 系统中的一个命令&#xff0c;用于显示或修改网络接口的地址信息。这个命令是 iproute2 软件包的一部分&#xff0c;通常在现代 Linux 发行版中都是预装的。 当你运行 ip addr 或 ip address 命令时&#xff0c;你会看到系统上所有网络接口的地…

ssh错误 ssh_exchange_identification: Connection closed by remote host

一 背景 今天使用终端ssh链接服务器报错&#xff0c;昨天还好的&#xff0c;今天就报错&#xff0c;原以为是服务器ip变了&#xff0c;但是同事使用原来ip可以链接&#xff0c;本人怀疑ssh链接人员是不是超出限制&#xff0c;于是沿着这思路解决&#xff0c;果然成功了。 二 …

.NET周刊【5月第2期 2024-05-12】

国内文章 C#在工业数字孪生中的开发路线实践 https://mp.weixin.qq.com/s/b_Pjt2oii0Xa_sZp_9wYWg 这篇文章探讨了C#在工业数字孪生技术中的应用&#xff0c;介绍了三种基于C#的数字孪生系统实现方案&#xff1a; WPF Unity&#xff1a;结合WPF技术和Unity引擎&#xff0c…

手撸XXL-JOB(一)——定时任务的执行

SpringBoot执行定时任务 对于定时任务的执行&#xff0c;SpringBoot提供了三种创建方式&#xff1a; 1&#xff09;基于注解(Scheduled) 2&#xff09;基于接口&#xff08;SchedulingConfigurer&#xff09; 3&#xff09;基于注解设定多线程定时任务 基于Scheduled注解 首…

MySQL表的基本操作

表 创建表 comment是添加一个注释 语法&#xff1a; 说明&#xff1a; field 表示列名 datatype 表示列的类型 character set 字符集&#xff0c;如果没有指定字符集&#xff0c;则以所在数据库的字符集为准 collate 校验规则&#xff0c;如果没有指定校验规则&#xff0c;则…

C++:编程领域的全能王者

在编程语言的海洋中&#xff0c;C以其全面而强大的功能&#xff0c;犹如一位全能王者&#xff0c;屹立不倒。它不仅在科技领域有着广泛的应用&#xff0c;更在推动社会进步、促进人类创新方面发挥着至关重要的作用。 一、C&#xff1a;编程界的璀璨明星 C自诞生以来&#xff…

Linux使用脚本删除多个版本的jar包

问题描述&#xff1a;在进行测试的过程中发现&#xff0c;有一个导出xls文件的功能&#xff0c;文件正常到导出来了&#xff0c;但是页面上显示的是中文&#xff0c;但是导出来的xls文件取的确是数据库的存值&#xff0c;没有转换 前端一看代码说没问题&#xff0c;那没办法重…

Android开发,日志级别

5个日志级别 Verbose (VERBOSE): 这是最低的日志级别&#xff0c;用于输出最为详尽的信息&#xff0c;包括开发和调试过程中的各种细节。在Log类中对应的方法是Log.v()。Debug (DEBUG): 此级别用于输出调试信息&#xff0c;帮助开发者理解程序运行流程或状态。通过Log.d()方法…

Mirror从入门到入神

Mirror从入门到成神 文章目录 Mirror从入门到成神简介NetworkClientRegisterPrefabConnect (string address)Disconnect ()activeactiveHost NetworkServerSpawn 简介 Mirror是一个unity网络同步框架&#xff0c;基于MonoBehaviour生命周期的回调的基础上进行数值的同步&#…

传输文件协议FTP与LFTP

目录 一.简介 二. FTP基础 主动模式&#xff08;Active Mode&#xff09;&#xff1a; 被动模式&#xff08;Passive Mode&#xff09;&#xff1a; 三. Vsftp 服务器简介 四. Vsftpd配置 1. 安装vsftpd&#xff08;ftp服务端&#xff09; 2.编辑配置文件 &#xff08;…