Lab2_Simple Shell_2020

news2025/8/3 23:43:19

Lab2: 实验目的:给xv6添加新的系统调用

并理解系统调用是如何工作的,并理解xv6内核的一些内部特征

实验准备:

  1. 阅读xv6的第2章以及第4章的4.3,4.3小节
  2. 熟悉下面的源码
  • 用户态相关的代码:user/user.huser/usys.pl
  • 内核态相关的代码:kernel/syscall.hkernel/syscall.h
  • 进程相关的代码:kernel/proc.kernel/proc.c

​ 3. 阅读KR大佬C语言书的如下章节

 2.9 (Bitwise operators) and 5.1 (Pointers and addresses) through 5.6 (Pointer arrays) and 6.4 (pointers to structures) by Kernighan and Ritchie (K&R)

任务1:系统调用跟踪功能 System call tracing (moderate)

In this assignment you will add a system call tracing feature that may help you when debugging later labs. You’ll create a new trace system call that will control tracing. It should take one argument, an integer “mask”, whose bits specify which system calls to trace. For example, to trace the fork system call, a program calls trace(1 << SYS_fork), where SYS_fork is a syscall number from kernel/syscall.h. You have to modify the xv6 kernel to print out a line when each system call is about to return, if the system call’s number is set in the mask. The line should contain the process id, the name of the system call and the return value; you don’t need to print the system call arguments. The trace system call should enable tracing for the process that calls it and any children that it subsequently forks, but should not affect other processes.

在本作业中,你需要添加一个系统调用跟踪功能,该功能可能会在以后调试实验室时对您有所帮助。您将创建一个新的“trace”系统调用来控制跟踪。它应该有一个参数,一个int 类型的“mask”,其bits位说明要跟踪的系统调用。例如,为了跟踪fork系统调用,程序调用trace(1 << SYS_fork),其中SYS_fork是来自kernel/syscall.h的系统调用号。如果在mask中设置了系统调用的编号,则必须修改xv6内核,以便在每个系统调用即将返回时打印出一行数据。该行应包含进程id、系统调用的名称和返回值;不需要打印系统调用参数。trace这个系统调用函数应启用对调用它的进程及其随后fork的任何子进程的跟踪,但不应影响其他进程。

我们提供了一个用户态的trace程序,它运行另一个启用了跟踪的程序(参见user/trace.c)。完成后,您应该看到如下输出:

$ trace 32 grep hello README
3: syscall read -> 1023
3: syscall read -> 966
3: syscall read -> 70
3: syscall read -> 0
$
$ trace 2147483647 grep hello README
4: syscall trace -> 0
4: syscall exec -> 3
4: syscall open -> 3
4: syscall read -> 1023
4: syscall read -> 966
4: syscall read -> 70
4: syscall read -> 0
4: syscall close -> 0
$
$ grep hello README
$
$ trace 2 usertests forkforkfork
usertests starting
test forkforkfork: 407: syscall fork -> 408
408: syscall fork -> 409
409: syscall fork -> 410
410: syscall fork -> 411
409: syscall fork -> 412
410: syscall fork -> 413
409: syscall fork -> 414
411: syscall fork -> 415
...
$ 

在上面的第一个例子中,trace调用grep tracing,而仅仅是read系统调用。32是1<<SYS_read(1<<5 = 2^5=32)的结果。在第二个例子中,trace在跟踪所有系统调用时运行grep;2147583647具有全部31个低位(2^31 = 2147583647)。在第三个示例中,程序没有被跟踪,因此没有打印跟踪输出。在第四个示例中,跟踪usertests(forkforkfork)程序中所有的fork系统调用(包含所有的子进程)。如果你的程序的输出如上所示,则解决方案是正确的(尽管进程ID可能不同)。

我们需要增加一个trace的系统调用,trace 接受一个int型参数,用来设置具体哪些系统调用函数需要跟踪到;

例如 trace 32意思SYS_read 函数需要被跟踪,32是1<<SYS_read(1<<5 = 2^5=32)的结果 ;

具体修改如下所示:

  • Add $U/_trace to UPROGS in Makefile

  • 运行make qemu,你将看到编译器无法编译user/trace.c,因为系统调用的用户空间存根还不存在:将系统调用的原型添加到user/user.h,将存根添加到user/usys.pl,以及对应的syscall编号添加到kernel/syscall.h。Makefile会先调用perl脚本user/usys.pl,它生成user/usys.S,即实际的系统调用存根,它使用RISC-v的ecall指令转换到内核。一旦你修复了编译问题,运行trace32 grep hello README;它将失败,因为您尚未在内核中实现系统调用。

    image-20210514153148919

    image-20210514153233553

    image-20210514153328513

  • kernel/sysproc.c中添加一个sys_trace() 函数,通过在proc结构体的新增加一个变量,我们这里用的是char mask[23]中记住其参数来实现新的系统调用(kernel/proc.h)。这个函数需要从用户空间检索系统调用的参数kernel/syscall.c中,你可以在kernel/sysproc.c中看到它们的使用示例。

    // kernel/sysproc.c 增加下面函数
    uint64
    sys_trace(void)
    {
      int n;
      // 获取n之后,如果小于0,也返回-1
      if(argint(0, &n) < 0 && n < 0)
        return -1;
      
      // 通过n控制哪些系统调用函数需要被trace 
      // n=32是`1<<SYS_read(1<<5 = 2^5=32)`的结果 
      // 反推的话,n>>1...22; 每次判断n是奇数还是偶数,可以判断当前位是多少
      // 这里可以用n&1=0 偶数 n&1=1 奇数 来判断奇数偶数
      struct proc *p=  myproc();
      char *mask =p->mask;
      for(int i = 0; i < 23; i++)// syscall num 从0开始
      {
        if(n&1)
          mask[i]='1';
        else
          mask[i]='0';
        n>>=1;
      }
    
      return 0;
    }
    
    

    image-20210514153512603

  • 修改fork()(参见kernel/proc.c)将trace mask参数从父进程复制到子进程。

    image-20210514153708281

  • 修改kernel/syscall.c中的syscall()函数以打印跟踪输出。你需要添加一个syscall名称数组来进行索引。

  // kernel/syscall.c
  void
  syscall(void)
  {
    int num;
    struct proc *p = myproc();
  
    num = p->trapframe->a7;
    if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
      p->trapframe->a0 = syscalls[num]();
      if('1' == p->mask[num]) // p->mask 只有trace的时候才会修改它,见kernel/sysproc.c->sys_trace
        printf("%d: syscall %s -> %d\n", p->pid, syscallnames[num], p->trapframe->a0);
    } else {
      printf("%d %s: unknown sys call %d\n",
              p->pid, p->name, num);
      p->trapframe->a0 = -1;
    }
  }

总结 遇到的一点小坑:

  1. "%d: syscall %s -> %d\n这里冒号后面需要加上空格,第一次没加上空格,死活匹配不过去,发现后被自己气死;

  2. trace children案例有超时时间,31s的时候就time out了,鉴于我ubuntu14,还是虚拟机,碍于性能问题总是跑超时,其实答案是对的,修改gradelib.py, 增加了判题的超时时间,就过去了;

    image-20210514155522566

贴下这道题的完结图:

image-20210514155627914

$ export PATH=$PATH:/home/moocos/riscv-gnu-toolchain/bin/bin //路径可以替换为你自己的路径
//然后编译xv6
$ make
$ make qemu
...
mkfs/mkfs fs.img README user/_cat user/_echo user/_forktest user/_grep user/_init user/_kill user/_ln user/_ls user/_mkdir user/_rm user/_sh user/_stressfs user/_usertests user/_wc user/_zombie user/_cow 
nmeta 46 (boot, super, log blocks 30 inode blocks 13, bitmap blocks 1) blocks 954 total 1000
balloc: first 497 blocks have been allocated
balloc: write bitmap block at sector 45
qemu-system-riscv64 -machine virt -kernel kernel/kernel -m 3G -smp 3 -nographic -drive file=fs.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0
hart 0 starting
hart 2 starting
hart 1 starting

Sysinfo (moderate)

In this assignment you will add a system call, sysinfo, that collects information about the running system. The system call takes one argument: a pointer to a struct sysinfo (see kernel/sysinfo.h). The kernel should fill out the fields of this struct: the freemem field should be set to the number of bytes of free memory, and the nproc field should be set to the number of processes whose state is not UNUSED. We provide a test program sysinfotest; you pass this assignment if it prints “sysinfotest: OK”.
在本作业中,您将添加一个系统调用sysinfo,用于收集有关正在运行的系统的信息。系统调用有一个参数:一个指向struct sysinfo的指针 (参见kernel/sysinfo.h)。内核应填写此结构的字段:freemem字段应设置为可用内存的字节数,nproc 字段应设置为状态 不是UNUSED的进程数。我们提供了一个测试程序sysinfotest;如果它输出“sysinfotest:OK”,则通过此作业。

一些提示:

  • 按照上次添加systrace的步骤,添加sysinfo
  • 收集空闲内存量,需要在kernel/kalloc.c中添加一个函数,这个函数来获取一个全局变量,标识内存量
  • 收集进程数,需要在kernel/proc.c中添加一个函数,这个函数来获取一个全局变量,标识进程数
  • 然后内核函数sysinfo(info)读取上面两个变量,然后将struct sysinfo 的值从内核空间拷贝到用户空间(copyout),完成调用

具体修改点如下

  • makefile增加_sysinfotest编译项:

    image-20230219114421145

  • user/usys.pl增加sysinfo的入口

    image-20230219114503105

  • user/user.h 增加sysinfo结构体和函数声明

image-20230219114533254

  • 创建user/sysinfo.c ,进入xv6可以执行,sysinfo查看系统内存,相当于新增一个shell函数

    image-20230219114752738

  • kernel/sysproc.c 增加头文件及sysinfo的函数实现,这里是内核态的实现,需要通过argaddr获取用户态调用的传参,并把内核态的数据通过copyout拷贝给用户态

    image-20230219114950679

image-20230219114938723

  • kernel/proc.c 返回系统的进程数量,推荐作法新增函数直接遍历获取。

    image-20230219120340958

  • kernel/kalloc.c 增加函数返回内存使用量

image-20230219120746946

  • kernel/def.h里增加函数声明

    image-20230219120829236

  • kernel/syscall.h 增加系统调用号

image-20230219115754429

  • kernel/syscall.c 增加syscalls和syscallnames的定义

image-20230219115902993

image-20230219120012942

补充trace的修改

  • kernel/proc.h 增加MASKSIZE=24的定义

image-20230219120049248

  • kernel/sysproc.c 同样补充遍历条件

    image-20230219115643648

最后执行结果

image-20230219121359818

一点点反思

侵入式修改_反例

不好的做法,反例:定义一个全局变量,申请的时候++,释放的时候–,侵入式修改,修改了原有代码,后续需求变化还会引起散弹式修改,不好不好

  • kernel/proc.c 返回系统的进程数量

image-20230219120230974

image-20230219120306096

image-20230219120313248

  • 返回系统可用内存

image-20230219120536056

image-20230219120548284

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

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

相关文章

第八章:枚举类与注解

第八章&#xff1a;枚举类与注解 8.1&#xff1a;枚举类的使用 ​ 类的对象只有有限个&#xff0c;确定的。我们称此类为枚举类。当需要定义一组常量是&#xff0c;强烈建议使用枚举类。如果枚举类中只有一个对象&#xff0c;则可以作为单例模式的实现方式。 如何定义枚举类 …

一图来看你需要拥有那些知识储备

技术实践 数据 关系型数据 MySQLSQLServerOraclePostgrSQLDB2 大数据存储 RedisMemcacheMongoDBHBaseHive 大数据处理 Hadoop 数据报表看板 DataGearGrafanaKibanaMetaBase 消息对列 Rabbit MQRock MQActive MQKafka 大数据搜索 SolrElasticSearchLucenHive 服务提…

HBase负载均衡的实现机制

数据库集群负载均衡的实现依赖于数据库的数据分片设计&#xff0c;可以在一定程度上认为数据分片就是数据读写负载&#xff0c;那么负载均衡功能就是数据分片在集群中均衡的实现。 一、Region迁移 作为一个分布式系统&#xff0c;分片迁移是最基础的核心功能。集群负载均衡、…

【实现“下一题”按钮的功能 Objective-C语言】

一、刚才我们把上半部分这个界面,给大家搭好了,懒加载也加载好了,接下来,我们要实现的就是点击“下一题”,是不是实现这个效果, 1.类似于图片浏览器这个效果呀, 来看一下,我们这里的这个数据懒加载已经加载起来了, 那么同时,我们界面上这些控件也都摆好了, 并且,…

商务会议租车价格受哪些因素影响!

有重要客户来访&#xff1f;要主办一次重要的会议&#xff1f;或者只是一次普通的机场接送。会议商务租车将能够根据您的需求为您提供全方位服务。 会议商务租车的概念 是指为满足企业商务活动的需要而提供的一种汽车服务。它主要包括&#xff1a;根据客户要求提供各种车型&…

黑马程序员提高变成

这里写目录标题函数模板1.2.2 函数模板注意事项1.2.3 函数模板案例调用规则类模板与函数模板区别类模板与继承类模板成员函数类外实现#pragma once类模板与友元案例重新定义【】stl2.2 STL基本概念STL六大组件容器算法迭代器初识vectorvector容器嵌套容器string容器string赋值操…

作为 React 开发者你应该知道的 7 个库

在成为一名全面的React开发人员的过程中&#xff0c;您会遇到无数的库&#xff0c;让您感到茫然和困惑。因此&#xff0c;这里列出了作为React开发人员学习不会出错的 7 个库。1.反应兜风React Joyride是一个库&#xff0c;可帮助您为React应用程序创建演练和导览。它是向现有用…

C++从头再来:知识点速通

1. 关于scanf 1.1 读入数字 scanf 的返回值表示成功输入的变量个数&#xff0c;当输入结束时&#xff0c;scanf将无法再次读取数据&#xff0c;返回0 # include <stdio.h> # include <math.h> # include <time.h># define M 1000000; // compute the max,…

99.【Git】

Git(一)、什么是版本控制1.什么是版本控制2、常见的版本控制工具(二)、版本控制分类1、本地版本控制2、集中版本控制 SVN3、分布式版本控制 Git(三)、Git与SVN的主要区别1、Git历史(四)、Git下载与环境配置1.git下载2、启动Git(五)、常用的Linux命令1.Linux常用命令(六)、Git必…

hadoop兼容性验证

前言 Hadoop是一个由Apache基金会所开发的分布式系统基础架构&#xff0c;主要解决海量数据的存储和海量数据的分析计算问题&#xff0c;广义上来说&#xff0c;Hadoop通常是指一个更广泛的概念–hadoop生态圈 Hadoop优缺点&#xff1a; 优点&#xff1a; 1、高可靠性&#x…

使用WebSocket、SockJS、STOMP实现消息实时通讯功能

客户端 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head><title>websocket client</title><script src"http://cdn.bootcss.com/sockjs-client/1.1.1/sockjs.min.js"></script>…

Softing smartLink网关——推进过程工业数字化转型

虽然在过程工业中各工厂所投入的运营时间千差万别&#xff0c;但仍需按照新标准来进行有效控制和管理&#xff0c;而这就需要使用一种能够聚合其异构数据的数字通信架构。对此&#xff0c;Softing提供了两种网关解决方案&#xff0c;可用于将过程工业通信架构集成到现有以太网系…

初次使用ESP32-CAM记录

模块的配置和图片 摄像头&#xff1a;8225N V2.0 171026 模块esp-32s 参考资料&#xff1a;https://docs.ai-thinker.com/esp32 配置环境 参考&#xff1a;https://blog.csdn.net/weixin_43794311/article/details/128622558 简单使用需要注意的地方 基本的环境配置和串口…

学习笔记:Java并发编程(补)ThreadLocal

【尚硅谷】学习视频&#xff1a;https://www.bilibili.com/video/BV1ar4y1x727【黑马程序员】学习视频&#xff1a;https://www.bilibili.com/video/BV15b4y117RJ 参考书籍 《实战 JAVA 高并发程序设计》 葛一鸣 著《深入理解 JAVA 虚拟机 | JVM 高级特性与最佳实践》 周志明 著…

大数据项目实战之数据仓库:用户行为采集平台——第3章 用户行为日志

第3章 用户行为日志 3.1 用户行为日志概述 用户行为日志的内容&#xff0c;主要包括用户的各项行为信息以及行为所处的环境信息。收集这些信息的主要目的是优化产品和为各项分析统计指标提供数据支撑。收集这些信息的手段通常为埋点。 目前主流的埋点方式&#xff0c;有代码…

流量与日志分析

文章目录1.流量与日志分析1.1系统日志分析1.1.1window系统日志与分析方法1.1.2linux 系统日志与分析方法1.2 web日志分析iis 日志分析方法apache日志分析**access_log****error_log**nginx日志分析tomcat 日志分析主流日志分析工具使用1.流量与日志分析 日志&#xff0c;是作为…

Dns域名解析服务器

前言 域名解析服务器的介绍 域名服务器的类型划分 DNS域名解析的过程 为什么需要DNS解析域名为IP地址&#xff1f; 通俗理解Dns DNS劫持 DNS污染 Dns面试经验 前言 DNS是一个应用层协议&#xff0c;用来获取域名对应的IP地址 域名解析服务器的介绍 DNS&#xff08;Dom…

大数据技术之HBase(二)HBase原理简介

一、HBase定义1.1 HBase定义HBase 是一种分布式、可扩展、支持海量数据存储的 NoSQL 数据库非结构化数据存储的数据库&#xff0c;基于列的模式存储。利用Hadoop HDFS作为其文件存储系统&#xff0c;写入性能很强&#xff0c;读取性能较差。利用Hadoop MapReduce来处理HBase中的…

HTTPS简介

HTTPS是HTTP开启TLS传输协议&#xff0c;客户端要拿到服务端的公钥&#xff0c;用公钥加密数据后再进行传输&#xff0c;防止数据泄露后背篡改。它要解决两个问题&#xff1a;怎么保证公钥可信怎么加密数据公钥可信问题客户端从服务端获取公钥的时候&#xff0c;存在请求被拦截…

Spring(一)Spring的7种事务传播行为

目录1.7种事务传播行为2.事务使用示例3.REQUIRES_NEW 事务传播行为使用示例3.1 事务传播图3.2 TUserAServiceImpl.java3.3 TUserBServiceImpl.java1.7种事务传播行为 Spring 中定义了七种事务传播行为&#xff0c;分别是&#xff1a;&#xff08;propagation&#xff1a;n.传播…