6.1810: Operating System Engineering Lab: Xv6 and Unix utilities By:Haostart

news2025/1/17 2:50:43

前言

开学后要为一年后找实习做准备了,准备打一下基础,就做一下MIT-6.S081,是2022版的,地址如下
6.1810: Operating System Engineering Lab

Lab

做实验前一定要清楚Xv6的系统调用有哪些!!!
在这里插入图片描述
做实验前一定要清楚Xv6的系统调用有哪些!!!

1. sleep

由于系统调用里已经有sleep了,所以处理一些其它细节即可.
注意参数量和合法性判断.
代码如下:

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

int main(int argc, char *argv[])
{
  if(argc != 2){
    fprintf(2, "sleep: missing operand\n");
    exit(1);
  }
  int time = atoi(argv[1]);//字符串数字类型转int型
  sleep(time);
  exit(0);
}

qemu的libc中有atoi实现:
在这里插入图片描述

2. pingpong

编写一个程序,使两个进程通过管道进行一次 “ping-pong”,即父进程向子进程发送一个字节,子进程读出后打印:received ping,随后再向父进程发送一个字节,父进程读入后打印:received pong,最后退出

做这个实验前必须要了解一下pipe,具体pipe实现可见 kernel/pipe.c 看下面的程序例子帮助理解

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

下面是book-riscv-rev3对程序的相关说明

该程序调用 pipe,创建一个新的管道,并将读取和写入文件描述符记录在数组 p 中。在 fork 之后,父进程和子进程都有引用管道的文件描述符。子进程调用 close 和 dup,将文件描述符零指向管道的读取端,关闭 p 中的文件描述符,并调用 exec 运行 wc。当 wc 从其标准输入读取时,它实际上是从管道读取的。父进程关闭管道的读取端,向管道写入数据,然后关闭写入端。
如果没有可用的数据,对管道的读取将等待数据被写入或者所有引用写入端的文件描述符被关闭;在后一种情况下,读取将返回 0,就像到达数据文件的末尾一样。读取阻塞直到不可能再有新数据到达是管道的一个原因,这也是为什么在执行上面的 wc 之前子进程重要地关闭管道的写入端的原因:如果 wc 的文件描述符之一指向了管道的写入端,wc 将永远不会看到文件结束符。

仿照这个例子,我们可以创建管道并进行一些简单的操作:

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

int
main(int argc,char *argv[]){

    int p[2],pid;
    char buff[2];

    pipe(p);	//系统调用,创建管道

    if(fork()==0){	// =0 为子进程
        pid=getpid();	//得到进程id

        read(p[0],buff,1);
        printf("%d: received ping\n", pid);
        close(p[0]);
        write(p[1],buff,1);
        close(p[1]);
        // printf("%d: send pong\n", pid);
        exit(0);
    }
    else{
        pid=getpid();
        write(p[1],buff+1,1);	//父进程写入管道
        close(p[1]);
        // printf("%d: send ping\n", pid);
        wait(0);	//等待子进程结束
        read(p[0],buff+1,1);
        close(p[0]);
        printf("%d: received pong\n", pid);
        exit(0);

    }
}

3. primes

编写一个使用管道实现的并发版本的素数筛法,这个想法是由Unix管道的发明者Doug McIlroy提出的。您的解决方案应该在文件user/primes.c中。

您的目标是使用管道和fork来建立管道线。第一个进程将数字2到35输入管道。对于每个素数,您将安排创建一个进程,该进程从其左侧邻居通过一个管道读取,并通过另一个管道写入其右侧邻居。由于xv6有限的文件描述符和进程数,第一个进程可以在35处停止。

一些提示:

  1. 要小心关闭进程不需要的文件描述符,否则在第一个进程到达35之前,您的程序会耗尽xv6的资源。
  2. 一旦第一个进程到达35,它应该等待整个管道终止,包括所有子进程、孙子进程等。因此,主要的素数进程只能在所有输出已被打印并且所有其他素数进程已退出后退出。
  3. 提示:当管道的写入端关闭时,read函数会返回零。
  4. 最简单的方法是直接向管道写入32位(4字节)整数,而不是使用格式化的ASCII I/O。
  5. 只有在需要时才应该创建管道中的进程。
  6. 将程序添加到Makefile中的UPROGS

第一次遇到这种,没有过算法经历很难搞,后说

4. find

编写一个简单版本的 UNIX 查找程序:查找目录树中特定名称的所有文件

写这个之前务必熟悉 ls.c 文件的代码,特别是ls函数的实现.
在这里插入图片描述
了解到如何得到文件各类信息后,就是实现问题了,我这里是使用了字符串匹配算法

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

//判断字符串包含
int
containsSubstring(const char *path, const char *target) {
    int pathLength = strlen(path);
    int targetLength = strlen(target);

    for (int i = 0; i <= pathLength - targetLength; i++) {
        int j;
        for (j = 0; j < targetLength; j++) {
            if (path[i + j] != target[j]) {
                break;
            }
        }
        if (j == targetLength) {
            return 1; // 找到了目标字符串
        }
    }
    return 0; // 没有找到目标字符串
}

//这里就是简单的返回文件名
char*
fmtname(char *path)
{
  static char buf[DIRSIZ+1];
  char *p;

  // Find first character after last slash.
  for(p=path+strlen(path); p >= path && *p != '/'; p--)
    ;
  p++;

  // Return blank-padded name.
  if(strlen(p) >= DIRSIZ)
    return p;
  memmove(buf, p, strlen(p));
  memset(buf+strlen(p), 0, DIRSIZ-strlen(p));
//   printf("buf:%s\n",buf);
  return buf;
}

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_DEVICE:
    // printf("it's a device.\n");
  case T_FILE:
    // printf("it's a file %s your find is %s\n",fmtname(path),target);
    if (containsSubstring(fmtname(path),target)) {
      printf("%s\n", path);
    }

    break;

  case T_DIR:
    while(read(fd, &de, sizeof(de)) == sizeof(de)){
        if(de.inum == 0)
            continue;
        if (strcmp(de.name, ".") == 0 || strcmp(de.name, "..") == 0)
            continue;
        // 构建新的路径
        strcpy(buf, path);
        p=buf+strlen(path);
        *p++='/';
        strcpy(p, de.name);
        // printf("%s\t\t%d\n",buf,strlen(buf));
        // strcat(buf, de.name);
        find(buf,target);	//递归实现
        memset(buf, 0, sizeof(buf));	//注意清空
    }
    break;
  }
  close(fd);
}

int
main(int argc, char *argv[])
{
  if(argc == 2){
    find(".",argv[1]);
    exit(0);
  }
  else if(argc==3){
    find(argv[1], argv[2]);
    exit(0);
  }
  else{
    fprintf(2, "Usage: find <path> <target>\n");
    exit(1);
  }
}

5. xargs

编写一个简单版本的UNIX xargs程序。xargs是组合多个命令的工具,简单来说,它可以通过管道接受字符串,并将接收到的字符串通过空格分割成许多参数,然后再将参数传递给其后面的命令,作为后面命令的命令行参数

在Unix-like操作系统的Shell中,管道符 |
用于创建管道(Pipe),它允许将一个命令的标准输出(stdout)连接到另一个命令的标准输入(stdin),从而实现进程间通信和数据传输。

原理如下:

  1. 当Shell解释器遇到 | 符号时,它会创建一个管道。

  2. Shell将要执行的命令分为两部分:左侧命令和右侧命令。左侧命令的标准输出被重定向到管道的写入端(管道的输出),右侧命令的标准输入被重定向到管道的读取端(管道的输入)。

  3. 接下来,Shell创建两个子进程,一个用于执行左侧命令,另一个用于执行右侧命令。

  4. 左侧命令的输出被写入管道,而右侧命令则从管道中读取输入数据。

  5. 这两个子进程并行运行,左侧命令生成的数据会通过管道传递给右侧命令,从而实现数据流的传输。

  6. 当左侧命令执行完毕后,它会关闭管道的写入端,这将触发右侧命令的结束条件。右侧命令会继续读取管道中的数据,直到没有更多数据可读。

  7. 当右侧命令执行完毕后,Shell会等待两个子进程都结束,并且会等待它们的退出状态。

简单来说就是左边的输出会变成右边的输入.
比如

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

左边输出hello too,附加到echo bye后面,变成echo bye hello too
下面是可以参考的代码

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

#define MAXARGS 128
#define MAXLEN  128

int main(int argc, char *argv[]) {
    if (argc < 2) {
        fprintf(2, "Usage: %s [command [initial-arguments]]\n", argv[0]);
        exit(1);
    }

    char *buf[MAXARGS];
    int j = 0;  
    for (int i = 1; i < argc; i++)
        buf[j++] = argv[i];     //这是原始的参数
    for (; j < MAXARGS; j++)    //为管道输入的参数准备内存空间
        buf[j] = (char *)malloc(sizeof(MAXLEN)); // Allocate memory for each argument

    j = argc - 1;   //表示管道输入的字符串的起始位置
    char buff;
    
    int cmd=0;

    while (1){	
      int m = 0;    //m为字符串字符索引
      while ((cmd=read(0, &buff, 1)) != 0) {
          if (buff == ' ') {	//空格则到下一个字符串
              buf[j++][m] = 0;
              m = 0;
          } else if (buff == '\n') {	//换行退出,每当读到\n就执行一条命令
              buf[j++][m] = 0;
              m=0;
              break;
          } else {		//参数字符读取
              buf[j][m++] = buff;
          }
      }
      buf[j] = 0;
      j=argc-1;		//字符串索引初始化
      int pid;
      if ((pid = fork()) == 0) {
        exec(buf[0], buf);
          
      } else if (pid < 0) {
          // Fork failed
          exit(1);
      } else {
          // Parent process
          wait(0);
      }
        if(cmd<=0)	//全部读取完毕,退出
        break;
    }

    exit(0);
}

make grade结果如下(不会primes埃氏筛):

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

总结

算是又一次熟悉了C语言的基础语法,特别是字符串相关的操作.
Xv6的系统调用简洁明了,结构清晰.适合初学者学习.
在算法方面我还有很大不足.在这里插入图片描述

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

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

相关文章

MySQL 开启配置binlog以及通过binlog恢复数据

目录 一、binlog日志基本概念二、开启binlog日志记录2.1、查看binlog日志记录启用状态2.2、开启配置binlog日志 三、制作测试数据&#xff08;可以先不执行&#xff0c;这里是为后续数据恢复做准备&#xff0c;先看数据恢复流程&#xff09;四、使用binlog日志恢复数据4.1、前置…

解决内网拉取企微会话存档代理问题的一种办法

问题&#xff1a;客户的服务都是内网的&#xff0c;不能直接访问外网&#xff1b;访问外网的话需要走kong网关才能出去。 会话存档官网说可以使用socket5、http方式拉取会话存档&#xff1b;我这边尝试了直接使用kong网关的ip和端口配置进去&#xff0c;是访问不了的 我后面就…

lazada、shopee卖家自养号测评的优势及环境搭建核心要点!

lazada、shopee卖家们都知道&#xff0c;流量对于店铺转化率具有非常重要的作用&#xff0c;要真正提升销售业绩和品牌知名度&#xff0c;仅仅依赖lazada、shopee内部的流量是不够的。这也就说明越来越多的卖家需要测评&#xff0c;用测评来提升产品的销量&#xff0c;留下好评…

MYSQL日志和事务原理

1)重做日志:在一个事务中会涉及到多个DML操作&#xff0c;修改的是在内存层面数据页中的数据&#xff0c;还没有及时的将修改之后的数据更新到磁盘中&#xff0c;真正的将更新后的数据写回到磁盘的时候才满足持久性&#xff0c;只是更新内存是不满足持久性的&#xff0c;如果只…

lv5 嵌入式开发-8 内存映射

目录 1 内存映射基本使用 1.1 内存映射概念 1.2 内存映射的使用 2 共享内存&#xff08;古老的 System V IPC&#xff09; 2.1 基本概念 2.2 共享内存使用步骤 2.3 共享内存使用 掌握&#xff1a;内存映射概念、内存映射使用、内存映射注意事项、了解SYSTEM V 共享内存概…

OV2640图像出现细小条纹问题

说明&#xff1a;条纹是随机生成的&#xff0c;有时候几根线&#xff0c;有时候10多根线 怀疑点&#xff1a; 1-走线&#xff0c;看网上说所有的时钟线和同步线不要交叉&#xff0c;不要太长没有验证 2-电源&#xff0c;1.2V和2.8的电源 刚开始以为是自己电路问题&#xff0c…

栈(Stack)和队列(Queue)

栈&#xff08;Stack&#xff09;和队列&#xff08;Queue&#xff09;都是常见的数据结构&#xff0c;用于存储和操作一组元素。 栈是一种后进先出&#xff08;Last-In-First-Out&#xff0c;LIFO&#xff09;的数据结构&#xff0c;类似于把元素堆在一起形成的一堆物体&…

PTA程序辅助实验平台——2023年软件设计综合实践_3(分支与循环)

第一题&#xff1a;7-1 印第安男孩 - C/C 分支与循环 朵拉编程的时候也想顺便练习英语。她编程从键盘读入一个整数n&#xff0c;如果n值为0或者1&#xff0c;向屏幕输出“0 indian boy.”或“1 indian boy.”&#xff1b;如果n大于1&#xff0c;比如9&#xff0c;则输出“9 in…

Ctfshow web入门 XSS篇 web316-web333 详细题解 全

CTFshow XSS web316 是反射型 XSS 法一&#xff1a; 利用现成平台 法二&#xff1a; 自己搭服务器 先在服务器上面放一个接受Cookie的文件。 文件内容&#xff1a; <?php$cookie $_GET[cookie];$time date(Y-m-d h:i:s, time());$log fopen("cookie.txt"…

SSM - Springboot - MyBatis-Plus 全栈体系(十四)

第三章 MyBatis 二、MyBatis 基本使用 1. 向 SQL 语句传参 1.1 mybatis 日志输出配置 mybatis配置文件设计标签和顶层结构如下&#xff1a; configuration&#xff08;配置&#xff09; properties&#xff08;属性&#xff09;settings&#xff08;设置&#xff09;typeAl…

7、SpringBoot_高级配置

一、配置高级 1.临时属性设置 1.1引出问题 如果目标电脑上8080端口已经使用&#xff0c;再次使用该端口会出现端口占用问题 解决方式 重新更换配置文件修改端口打包通过临时属性配置新端口更换配置文件 1.2添加临时属性配置 通过临时属性修改8080端口 java -jar 项目.jar…

zemax目镜调焦演示

目镜的焦距应当是可变的&#xff0c;就像我们使用显微镜时&#xff0c;可以通过旋转调节旋钮&#xff0c;使得图像变得清晰&#xff0c;目镜的焦距也可以调整&#xff0c;使得要被观察的像出现在合适的位置。 这样&#xff0c;不同的人都可以使用同一台仪器&#xff0c;不管近…

Spring5应用之AOP额外功能详解

作者简介&#xff1a;☕️大家好&#xff0c;我是Aomsir&#xff0c;一个爱折腾的开发者&#xff01; 个人主页&#xff1a;Aomsir_Spring5应用专栏,Netty应用专栏,RPC应用专栏-CSDN博客 当前专栏&#xff1a;Spring5应用专栏_Aomsir的博客-CSDN博客 文章目录 前言MethodBefore…

Doris数据库FE——启动流程源码详细解析

Doris中FE主要负责接收和返回客户端请求、元数据以及集群管理、查询计划生成等工作。代码路径&#xff1a;doris/fe/fe-core/src/main/java/org/apache/doris/DorisFE.java 环境检查 在启动FE的时候&#xff0c;主要做环境检查。检查一些启动时必要的环境变量以及初始化配置…

社区团购新零售搭伙拼团小程序源码(前后端)

社区团购新零售搭伙拼团小程序源码是一款非常实用的电商小程序&#xff0c;它包含了前后端文件&#xff0c; 可以快速地进行部署和使用。该小程序是基于微信小程序开发的&#xff0c;支持社区团购、新零售、搭伙拼团等多种功能。 该小程序具有良好的用户体验&#xff0c;包括…

范数Norm-衡量向量大小的方法

性质 非负性: 范数的值总是非负的,且当且仅当向量全为零时,范数的值为零。 齐次性: 对于任意实数α,有 三角不等式: 对于任意向量x和y,有 常见范数 L1: 向量所有元素绝对值的和,权重稀疏 L2:欧几里得范数,权重平滑 无穷范数:表示向量中最大的元素 为什么使用范…

英飞凌 Tricore 架构中断系统详解

本文以TC3系列MCU为例&#xff0c;先来了解中断源是如何产生的&#xff0c;再看一下CPU是如何处理中断源的。 AURIX TC3XX的中断路由模块 Interrupt Router (IR) 在TC3中&#xff0c;中断既可以被CPU处理&#xff0c;也可以被DMA处理&#xff0c;所以手册中不再把中断称为中断…

vue3硅谷甄选02 | 封装svg组件 - axios二次封装

文章目录 vue3硅谷甄选02功能1&#xff1a;封装svg组件SVG图标配置svg封装成组件svg组件注册为全局组件自定义统一注册全局组件的插件自定义插件的原理插件的使用 app.use(plugin, [options]) 功能2&#xff1a;axios二次封装使用mock插件构造数据axios二次封装api接口统一管理…

【大数据开发技术】实验01-Hadoop安装部署

文章目录 Hadoop安装部署一、实验目标二、实验要求三、实验内容四、实验步骤附&#xff1a;系列文章 Hadoop安装部署 虚拟机数量&#xff1a;3 系统版本&#xff1a;Centos 7.5 Hadoop版本&#xff1a; Apache Hadoop 2.7.3 主节点信息&#xff1a; 操作系统&#xff1a;Cen…

Tomcat 与 JDK 对应版本关系

对应关系 Tomcat版本 jdk版本11.0.x JDK 21及以后10.1.x JDK11及以后10.0.xJDK1.8及以后9.0.x JDK1.8及以后8.5.xJDK1.7及以后8.0.x JDK1.7及以后 查看对应关系方法&#xff1a; 登陆Tomcat官网&#xff1a;Apache Tomcat - Welcome! 结果&#xff1a;