MIT6.S081操作系统实验2021(xv6系统)——lab1 Xv6 and Unix utilities

news2024/9/22 15:50:18

MIT6.S081操作系统实验2021——lab1

参考文章

sleep

要求为xv6实现UNIX 程序sleep;其应该暂停用户指定的ticks number。tick是 xv6 内核定义的时间概念,即计时器芯片的两次中断之间的时间(两次时钟中断之间的时间)。您的解决方案应该在文件user/sleep.c中。

  • 如果用户忘记传递参数,sleep 应该打印一条错误消息。
  • 确保main调用exit()以退出程序。
  • 在 Makefile 中将你的sleep程序添加到UPROGS;完成后,make qemu将编译您的程序,您将能够从 xv6 shell 运行它。

Run the program from the xv6 shell:

 $ make qemu
 ...
 init: starting sh
 $ sleep 10
 (nothing happens for a little while)
 $

提示

  • 查看 user/ 中的一些其他程序( 例如user/echo.c、user/grep.c和user/rm.c),了解如何获取传递给程序的命令行参数。(argc是包括程序名本身的参数个数,argv是包括程序名本身在内的所有参数的字符串指针数组)
  • 命令行参数作为字符串传递;您可以使用atoi将其转换为int(请参阅 user/ulib.c)。
  • 使用系统调用sleep。

思路
实现首先检查参数个数,然后把参数中给出的ticks number转变为int,之后使用sleep系统调用,输出提示信息即可。

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

int main(int argc,char *argv[])
{
    if(argc == 1) 
    {
        printf("sleep need one argument\n");
        exit(1);
    }
    uint ticks = atoi(argv[1]);
    sleep(ticks);
    printf("(nothing happens for a little while)");
    exit(0);
}

在这里插入图片描述
在这里插入图片描述

pingpong

要求编写一个程序,使用 UNIX 系统调用通过一对管道在两个进程之间“pingpong”一个字节,一个管道用于每个方向。父进程应该向子进程发送一个字节;子进程应该打印“: received ping”,其中 是它的进程 ID,将管道上的字节写入父进程,然后退出;父进程应该从子进程那里读取字节,打印“: received pong”,然后退出。您的解决方案应该在文件user/pingpong.c中。

Run the program from the xv6 shell and it should produce the following output:

$ make qemu
 ...
 init: starting sh
 $ pingpong
 4: received ping
 3: received pong
 $

提示

  • 使用pipe创建管道。
  • 使用fork创建一个子进程。
  • 使用read从管道读取,并使用write写入管道。
  • 使用getpid查找调用进程的进程 ID。
  • 将程序添加到Makefile 中的UPROGS。
  • xv6 上的用户程序有一组有限的库函数可供他们使用。您可以在 user/user.h中看到声明;定义(系统调用除外)位于user/ulib.c user/printf.c和user/umalloc.c中。

思路

  • 父进程关闭管道1读端,向写端写入ping,关闭管道1写端。关闭管道2写端,从管道2读端读出pong,关闭管道2读端。使用wait让子进程先打印ping,然后打印pong.
  • 子进程关闭管道1写端,从管道1读端读出ping,关闭管道1读端。关闭管道2读端,向写端写入pong,关闭管道2写端,然后打印ping。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"



#define READ 0
#define WRITE 1


int main(int argc, char *argv[])
{
    if(argc != 1) printf("dont input arguments\n");

    int pipeParToChr[2];
    int pipeChrToPar[2];
    char buf[8];

    pipe(pipeParToChr);
    pipe(pipeChrToPar);

    int pid = fork();
    if( pid == 0 ) // 子进程
    {
        close(pipeParToChr[WRITE]);
        read(pipeParToChr[READ] , buf , sizeof(buf));
        close(pipeParToChr[READ]);

        close(pipeChrToPar[READ]);
        write(pipeChrToPar[WRITE],"pong\n",5);
        close(pipeChrToPar[WRITE]);

        printf("%d: received %s",getpid(),buf);
        exit(0);

    }else  // 父进程
    {
        close(pipeParToChr[READ]);
        write(pipeParToChr[WRITE] , "ping\n" , 5);
        close(pipeParToChr[WRITE]);

        close(pipeChrToPar[WRITE]);
        read(pipeChrToPar[READ],buf,sizeof(buf));
        close(pipeChrToPar[READ]);
        wait((int*)0);
        printf("%d: received %s",getpid(),buf);
        exit(0);
    }
}

在这里插入图片描述
在这里插入图片描述

primes

要求
使用管道编写一个并发版本的初筛。这个想法归功于 Unix 管道的发明者 Doug McIlroy。本页中间的图片和周围的文字说明了如何做到这一点。您的解决方案应该在文件user/primes.c 中。您的目标是使用pipe和fork来设置管道。第一个过程将数字 2 到 35 输入管道。对于每个素数,您将安排创建一个进程,该进程通过管道从其左邻居读取并通过另一管道向其右邻居写入。由于 xv6 的文件描述符和进程数量有限,第一个进程可以在 35 处停止。

Your solution is correct if it implements a pipe-based sieve and produces the following output:

$ make qemu
 ...
 init: starting sh
 $ primes
 prime 2
 prime 3
 prime 5
 prime 7
 prime 11
 prime 13
 prime 17
 prime 19
 prime 23
 prime 29
 prime 31
 $

提示

  • 尽早关闭进程不需要的文件描述符,否则你的程序将在第一个进程达到 35 之前耗尽 xv6 的资源。
  • 一旦第一个进程达到 35,它应该等到整个程序终止,包括所有子孙进程。因此,主进程(产生2~35数字的进程)应该只在所有输出都被打印出来,并且在所有其他子孙进程都退出之后才退出。
  • 提示:当管道的写端关闭时,读取该管道的read()立即返回零。
  • 将 32 位(4 字节)的int直接写入管道是最简单的,而不是使用格式化的 ASCII I/O。
  • 您应该仅在需要时在管道流水线中创建进程。
  • 将程序添加到Makefile 中的UPROGS。

实现
并发的素数筛模型:根据该图,我们可以看出需要一个主进程将2~35的int数据输入到第一个子进程。而从子进程的视角看,所有子进程的任务都是从父进程那里读取数据。将读到的第一个数据print出来,并以此为基,将所有读到的不是它的倍数的数据传递给自己的子进程。此外,流水线停止的条件是一个子进程从父进程那里读不到数据,说明所有数据已经筛选完毕。请添加图片描述
思路

  • 父进程输出一个数组和数组的个数
  • 子进程接收数组,接收数组的第一个为最小素数,为基数。输出
  • 之后数据分别判断是否为基数的整数倍,如果是抛弃,不是则给下一级子进程
  • 最后实现递归,当输入数组的元素个数为1时,输出,结束进程
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

#define READ 0
#define WRITE 1

void function(int num[] , int size)
{
    int pipe1[2];
    pipe(pipe1);
    int pid = fork();
    if(pid > 0)
    {
        close(pipe1[READ]);
        
        for(int i=0; i<size; i++)
        {
            write(pipe1[WRITE],&num[i],sizeof(num[i]));
        }
        close(pipe1[WRITE]);
        wait((int*)0);
    }else
    {
        close(pipe1[WRITE]);
        int numchr[34] , indnx = 0;
        int tmp , min;
        while(read(pipe1[READ] ,&tmp,sizeof(tmp) ))
        {   
            if(indnx == 0) 
            {
                min = tmp;
                printf("prime %d\n", min);
                indnx++;
            }

            if(tmp%min != 0) 
            {
                numchr[indnx-1] = tmp;
                indnx++;
            }
        }
        close(pipe1[READ]);
        function(numchr,indnx-1);
        exit(0);
    }
}

int main(int argc, char *argv[])
{
    int num[34];
    int indnx = 0;
    for(int i = 2; i <= 35 ;i++)
    {
        num[indnx] = i;
        indnx++;
    }
    function(num,34);
    exit(0);
}

find

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

Your solution is correct if produces the following output (when the file system contains the files b and a/b):

$ make qemu
...
init: starting sh
$ echo > b
$ mkdir a
$ echo > a/b
$ find . b
./b
./a/b
$ 

即find的功能是在argv[1]所给目录和它的所有子目录(不包括.和…)中查找名为argv[2]的文件。

提示

  • 查看 user/ls.c 以了解如何读取操纵目录。
  • 使用递归允许 find 进入到子目录。
  • 不要递归到 . 和 …
  • 对文件系统的更改在 qemu 运行中持续存在;运行干净的文件系统make clean,然后make qemu.
  • 用 strcmp()比较字符串。
  • 将程序添加到Makefile 中的UPROGS。

为了支持递归,我们需要实现一个在给定目录中查找给定文件的find()函数:

void find(char *path,char *name);

提示告诉我们研究ls.c来了解对目录和文件的读取,其中主要是函数void ls(char *path):

首先以读的方式打开path这个file(unix中一切皆file,包括普通文件、目录、设备),返回一个fd.接着使用fstat()把这个file的一些文件系统相关的信息填充进st结构体中。

对于要实现的函数void find(char *path,char *name),path必为目录,则直接仿照ls中case T_DIR的情况处理就行。和ls不同的是,我们不需要输出所有文件的所有信息,即在stat(buf, &st)之后,我们不需要printf出文件的信息,而是进行筛选,当这个file类型是文件并且名字与要找的name相同,则输出buf中存储的相对路径即可。如果这个file类型是目录并且不是.和…,就进入此目录递归查找。

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

void find(char *path , char *target)
{
    char buf[512], *p;
    int fd;
    struct dirent de;
    struct stat st;

    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)
    {
        case T_FILE:
            if(strcmp( path + strlen(path) - strlen(target) , target ) ==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;
                memmove(p, de.name, DIRSIZ);
                p[DIRSIZ] = 0;
                if(stat(buf, &st) < 0)
                {
                    printf("find: cannot stat %s\n", buf);
                    continue;
                }

                if(strcmp(".", de.name) != 0 && strcmp("..", de.name) != 0)
                {
                    find(buf, target);
                }
            }
            break;
  }
  close(fd);

}


int main(int argc, char *argv[])
{
    if(argc != 3)
    {
        printf("input arguments : find <path> <file name>\n");
        exit(1);
    }
   
    find(argv[1], argv[2]);
    exit(0);
}

在这里插入图片描述
在这里插入图片描述

xargs

要求编写一个简单版本的 UNIX xargs 程序:从标准输入读取行。为每一行将该行作为参数提供给命令,并运行命令。您的解决方案应该在文件user/xargs.c中。
以下示例说明了 xargs 的行为:

$ echo hello too | xargs echo bye
bye hello too
$

注意这里的命令是“echo bye”,附加参数是“hello too”,使得命令“echo bye hello too”输出“bye hello too”。

提示

  • 使用fork和exec对每一行输入调用命令。在父进程中使用wait等待子进程完成命令。
  • 要读取单行输入,请一次读取一个字符,直到出现换行符 (‘\n’)。
  • kernel/param.h 声明了 MAXARG,如果您需要声明 argv 数组,这可能很有用。
  • 将程序添加到Makefile 中的UPROGS。

实现

  • 我们已经知道,shell中可以使用管道 | 将’|‘之前的命令的标准输出作为之后的命令的标准输入。而xargs的作用是将’| xargs’之前的命令的标准输出作为之后的命令的附加的命令行参数。标准输入和命令行参数,有很大的不同。
  • 以$ echo hello too | xargs echo bye为例,其argc为3。即实际的命令行参数从xargs开始算起。
  • 程序的功能应是维护一个新的new_argv数组,先从argv[1]开始把本来的参数复制到new_argv中,然后从stdin(文件描述符0)读取字节,每读取到’\0’就把这个新参数添加到new_argv末尾,读取到’\n’就fork出一个子进程以new_argv为基础使用exec来执行命令。然后从本来的参数末尾重新开始添加参数。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/param.h"

int main(int argc, char *argv[]) 
{
    char *new_argv[MAXARG];
    int cur_argv = 1;
    for( cur_argv = 1 ;cur_argv <= argc - 1 ; cur_argv++)
    {
        new_argv[cur_argv - 1] = argv[cur_argv];
    }
    
    char ch;
    char buf[128];
    char *cur_buf = buf;
    new_argv[cur_argv-1] = buf;
   
    while(read(0,&ch,sizeof(char)))
    {
        if( ch == ' ')
        {
            *cur_buf = '\0';
            cur_buf++;
            new_argv[cur_argv ] = cur_buf;
            cur_argv++;
        }else if( ch == '\n' )
        {
            *cur_buf = '\0';
            new_argv[cur_argv] = 0;

            int pid = fork();
            if( pid == 0)
            {
                exec(new_argv[0] , new_argv);
                exit(0);
            }else
            {
                wait((int*)0);
                cur_buf = buf;
                cur_argv = argc;
            }
        }else
        {
            *cur_buf = ch;
            cur_buf++;
        }
    }
   
    exit(0);
}

在这里插入图片描述
在这里插入图片描述

makefile

在这里插入图片描述

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

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

相关文章

从服务员到高级测试工程师,我的坎坷之路谁又能懂

首先要感谢那些嘲讽我代码写的烂的人&#xff0c;五年开发经验嘲笑刚出校门踏入社会的我&#xff0c;让我放弃了开发工作&#xff0c;走向测试的康庄大道。此外&#xff0c;曾经的开发经验对我测试工作的帮助是无与伦比的。数据库&#xff0c;编程语言&#xff0c;liunx&#x…

Linux驱动开发——高级I/O操作(五)

目录 mmap设备文件操作 定位操作 习题 mmap设备文件操作 显卡一类的设备有一片很大的显存&#xff0c;驱动程序将这片显存映射到内核的地址空间&#xff0c;方便进行操作。如果用户想要在屏幕上进行绘制操作&#xff0c;将要在用户空间开辟出一片至少一样大的内存&#xff…

Python学习之DateTime、TimeDelta、Strftime(Format)及其示例

文章目录 前言一、pandas是什么&#xff1f;二、使用步骤1.引入库2.使用date.today()打印日期3.Python当前日期和时间&#xff1a;now() today()总结 4.如何使用 Strftime()格式化日期和时间输出1. 首先&#xff0c;我们将看到一个简单的如何格式化年份的步骤。通过一个例子来理…

使用buildroot编译完整系统【IMX6ULLPRO】

目录 构建 IMX6ULL Pro 版的根文件系统 编译系统 ​编辑 镜像文件 构建 IMX6ULL Pro 版的根文件系统 配置文件说明 编译系统 下面以 100ask_imx6ull_pro_ddr512m_systemV_qt5_defconfig 配置文 件为例&#xff0c;在 ubuntu 终端上说明 Buildroot 的配置过程&#x…

玩转ChatGPT:辅助编程

一、写在前面 首先让小Chat介绍自己在编程方面的天赋&#xff1a; 总结起来&#xff1a;TA掌握了海量的编程知识&#xff0c;能做到自动代码生成、代码审查优化、编程教学辅导以及实时问题解答。我问TA学习了多少案例&#xff0c;TA说&#xff1a;忘了&#xff0c;但保证够用。…

OpenCV-手势语言识别

OpenCV-手势语言识别 OpenCV-手势语言识别Python环境、TensorFlow环境设置直方图模型保存set_hand_hist.py相关代码如下&#xff1a;载入手势图片 OpenCV-手势语言识别 本部分包括Python环境、TensorFlow环境和OpenCV-Python环境。 Python环境、TensorFlow环境 需要Python 3…

Spring Boot-入门、热部署、配置文件、静态资源

Spring Boot Spring Boot概述 一、微服务概述 1、微服务 微服务&#xff08;Microservices&#xff09;是一种软件架构风格。微服务是以专注单一责任与功能的小型功能区块 &#xff08;Small Building Blocks&#xff09;为基础&#xff1b;利用模块化的方式组合出复杂的大…

DockerImage镜像版本说明

参考文章 1、https://medium.com/swlh/alpine-slim-stretch-buster-jessie-bullseye-bookworm-what-are-the-differences-in-docker-62171ed4531d 2、https://stackoverflow.com/questions/52083380/in-docker-image-names-what-is-the-difference-between-alpine-jessie-stret…

顺序表 和 链表 的区别

顺序表 基于数组 就是对数组进行相关的操作 进行存储数据 数组有个很大的缺点就是 可能会产生内存浪费 针对数组这一缺点 就产生了链表 链表顾名思义 就是像链条一样将数据串起来 链表是将内存中的小空间利用起来 让内存的利用率提高 但是也产生了很大的缺点 就是不能随…

数据通信基础 - 差错控制(奇偶校验、海明码、CRC循环冗余校验码)

文章目录 1 概述1.1 检错和纠错1.2 差错控制原理 2 差错控制的方法2.1 奇偶校验2.2 海明码2.3 CRC循环冗余校验码 3 扩展3.1 网工软考真题 1 概述 1.1 检错和纠错 无论通信系统如何可靠&#xff0c;都不能做到完美无缺。因此&#xff0c;必须考虑怎样发现和纠正信号传输中的差…

【实用教程】教你制作好看的论文区位图

区位图是反映目标区所在位置和与周边地区自然、经济相互作用关系的体现区位的地图。好的区位图能为论文打好专业的基调&#xff0c;给读者留下更好观感&#xff0c;是科研论文不可或缺的一部分。 层次&#xff1a;面向国际期刊的区位图一定要从国际角度出发&#xff0c;清楚表示…

第一性原理差分电荷密度计算能得到什么数据?

第一性原理差分电荷密度&#xff08;DFT&#xff09;计算是一种用于计算分子和材料结构的计算化学方法。它基于物理学原理和量子力学理论&#xff0c;通过解决薛定谔方程来计算电子在分子和材料结构中的行为。DFT是一种非常重要的计算方法&#xff0c;因为它可以提供关于分子和…

自媒体必备素材库,免费、商用,赶紧马住~

自媒体经常需要用到各类素材&#xff0c;本期就给大家安利6个自媒体必备的素材网站&#xff0c;免费、付费、商用都有&#xff0c;建议收藏起来~ 1、菜鸟图库 https://www.sucai999.com/video.html?vNTYwNDUx 菜鸟图库可以找到设计、办公、图片、视频、音频等各种素材。视频素…

托福高频真词List03 // 附阅读真题

目录 4月23日单词 4月23日真题 4月23日单词 adjacentneighboringnearbyadj 毗邻的dependablereliableadj 可靠的 chronology a list that pairs past events with dates n 年表remarkably closeextremely close极为接近competenceabilityn 才能supplementadd tov 补充supplem…

Linux_红帽8学习笔记分享_6

Linux_红帽8学习笔记分享_6 文章目录 Linux_红帽8学习笔记分享_61. RPM软件包的使用技巧1.1如何查询指定软件包是否安装1.2如何删除指定软件包1.3如何安装指定软件包1.5依赖关系 2. YUM软件仓库的配置及使用2.1修改YUM软件仓库的配置文件 3.YUM常见命令使用技巧3.1查询指定软件…

Django框架之创建项目、应用并配置数据库

django3.0框架创建项目、应用并配置数据库 创建项目 进入命令行 新建一个全英文的目录 进入目录 输入命令 django-admin startproject project 项目目录层级 查看当前目录层级 tree /f 目录文件说明 创建数据库 做一个学生管理系统做演示&#xff0c;使用navicat创建数据…

【LPC55s69】使用FAL分区管理与easyflash变量管理

文章目录 1.FAL组件1.1什么是FAL1.2 使用ENV配置FAL1.3 FAL SFUD 移植1.4 FAL SFUD 测试用例1.5 测试结果 2.DFS文件系统2.1 什么是DFS2.2 DFS架构2.3 使用ENV配置DFS2.4 DFS挂载到FAL分区测试2.5 测试结果 3.Easyflash移植到FAL分区3.1 简述EasyFlash3.2EasyFlash软件包使用3.…

Docker的数据管理(dockerfile等)

文章目录 一、管理docker容器中数据1、 数据卷2、数据卷容器 二、容器互联&#xff08;使用centos镜像&#xff09;三、Docker 镜像创建1、基于现有镜像创建2、基于本地模板创建3、基于Dockerfile创建 四、Dockerfile操作常用的指令:五、Dockeerfile案例 一、管理docker容器中数…

HNU数据结构与算法分析-小班7

HNU数据结构与算法分析-小班7

【Python】可视化KITTI激光雷达点云绘制立体框

前言 最近在复现PointRCNN论文过程中发现没有可视化代码&#xff0c;于是查找资料找到了&#xff1a; pointRCNN 结果可视化——tiatiatiatia 按照参考链接下载代码【轮子库】并可视化成功了 mayavi库的学习 主要是利用了mlab模块&#xff1a; Mayavi2 旨在提供一个简单…