205.Mit6.S081-实验二 system calls

Lab2:system calls

        在上一个实验室中,您使用系统调用编写了一些实用程序。在本实验室中,您将向xv6添加一些新的系统调用,这将帮助您了解它们是如何工作的,并使您了解xv6内核的一些内部结构。您将在以后的实验室中添加更多系统调用。

一、实验准备

在你开始写代码之前,请阅读xv6手册《book-riscv-rev1》的第2章、第4章的第4.3节和第4.4节以及相关源代码文件:

  • 系统调用的用户空间代码在user/user.huser/usys.pl中。
  • 内核空间代码是kernel/syscall.hkernel/syscall.c
  • 与进程相关的代码是kernel/proc.hkernel/proc.c

可看这一篇博客来增加理解。

158.xv6——2(system calls)-CSDN博客

要开始本章实验,请将代码切换到syscall分支:

$ git fetch
$ git checkout syscall
$ make clean

如果运行make grade,您将看到测试分数的脚本无法执行tracesysinfotest。您的工作是添加必要的系统调用和存根(stubs)以使它们工作。

二、System call tracing实验(moderate)

 1.需求

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

        我们提供了一个用户级程序版本的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,仅跟踪了read系统调用。321<<SYS_read。在第二个示例中,trace在运行grep时跟踪所有系统调用;2147483647将所有31个低位置为1。在第三个示例中,程序没有被跟踪,因此没有打印跟踪输出。在第四个示例中,在usertests中测试的forkforkfork中所有子孙进程的fork系统调用都被追踪。如果程序的行为如上所示,则解决方案是正确的(尽管进程ID可能不同)

2.提示:

  • MakefileUPROGS中添加$U/_trace
  • 运行make qemu,您将看到编译器无法编译user/trace.c,因为系统调用的用户空间存根还不存在:将系统调用的原型添加到user/user.h,存根添加到user/usys.pl,以及将系统调用编号添加到kernel/syscall.hMakefile调用perl脚本user/usys.pl,它生成实际的系统调用存根user/usys.S,这个文件中的汇编代码使用RISC-V的ecall指令转换到内核。一旦修复了编译问题(注:如果编译还未通过,尝试先make clean,再执行make qemu),就运行trace 32 grep hello README;但由于您还没有在内核中实现系统调用,执行将失败。
  • kernel/sysproc.c中添加一个sys_trace()函数,它通过将参数保存到proc结构体(请参见kernel/proc.h)里的一个新变量中来实现新的系统调用。从用户空间检索系统调用参数的函数在kernel/syscall.c中,您可以在kernel/sysproc.c中看到它们的使用示例。
  • 修改fork()(请参阅kernel/proc.c)将跟踪掩码从父进程复制到子进程。
  • 修改kernel/syscall.c中的syscall()函数以打印跟踪输出。您将需要添加一个系统调用名称数组以建立索引。

3.trace实现

本实验主要是实现一个追踪系统调用的函数,那么首先根据提示定义trace系统调用,并修复编译错误。

首先看一下user/trace.c的内容,主要的代码如下

// 调用 trace 系统调用,并传递第一个命令行参数的整数值
if (trace(atoi(argv[1])) < 0) {
    // 如果 trace 调用失败,打印错误信息并退出
    fprintf(2, "%s: trace failed\n", argv[0]);
    exit(1);
}

// 将命令行参数从 argv[2] 开始复制到 nargv 数组中
for (i = 2; i < argc && i < MAXARG; i++) {
    nargv[i-2] = argv[i];
}

// 使用 exec 系统调用执行 nargv[0] 命令,并传递 nargv 数组作为参数
exec(nargv[0], nargv);

它首先调用trace(int),然后将命令行中的参数argv复制到nargv中,同时删去前两个参数,例如

argv  = trace 32 grep hello README
nargv = grep hello README

那么,根据提示,我们首先再proc结构体中添加一个数据字段,用于保存trace的参数。并在sys_trace()的实现中实现参数的保存

// kernel/proc.h
struct proc {
  // ...
  int trace_mask;    // trace系统调用参数
};

// kernel/sysproc.c
uint64
sys_trace(void)
{
  // 获取系统调用的参数
  argint(0, &(myproc()->trace_mask));
  return 0;
}

由于struct proc中增加了一个新的变量,当fork的时候我们也需要将这个变量传递到子进程中(提示中已说明)

//kernel/proc.c
int
fork(void)
{
  // ...

  safestrcpy(np->name, p->name, sizeof(p->name));

  //将trace_mask拷贝到子进程
  np->trace_mask = p->trace_mask;

  pid = np->pid;
  // ...

  return pid;
}

接下来应当考虑如何进行系统调用追踪了,根据提示,这将在syscall()函数中实现。下面是实现代码,需要注意的是条件判断中使用了&而不是==,这是因为在实验说明书的例子中,trace 2147483647 grep hello README将所有31个低位置为1,使得其可以追踪所有的系统调用。

void
syscall(void)
{
  int num;
  struct proc *p = myproc();

  num = p->trapframe->a7;  // 系统调用编号,参见书中4.3节
  if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
    p->trapframe->a0 = syscalls[num]();  // 执行系统调用,然后将返回值存入a0

    // 系统调用是否匹配
    if ((1 << num) & p->trace_mask)
      printf("%d: syscall %s -> %d\n", p->pid, syscalls_name[num], p->trapframe->a0);
  } else {
    printf("%d %s: unknown sys call %d\n",
            p->pid, p->name, num);
    p->trapframe->a0 = -1;
  }
}

在上面的代码中,我们还有一些引用的变量尚未定义,在syscall.c中定义他们

// ...
extern uint64 sys_trace(void);

static uint64 (*syscalls[])(void) = {
// ...
[SYS_trace]   sys_trace,
};

static char *syscalls_name[] = {
[SYS_fork]    "fork",
[SYS_exit]    "exit",
[SYS_wait]    "wait",
[SYS_pipe]    "pipe",
[SYS_read]    "read",
[SYS_kill]    "kill",
[SYS_exec]    "exec",
[SYS_fstat]   "fstat",
[SYS_chdir]   "chdir",
[SYS_dup]     "dup",
[SYS_getpid]  "getpid",
[SYS_sbrk]    "sbrk",
[SYS_sleep]   "sleep",
[SYS_uptime]  "uptime",
[SYS_open]    "open",
[SYS_write]   "write",
[SYS_mknod]   "mknod",
[SYS_unlink]  "unlink",
[SYS_link]    "link",
[SYS_mkdir]   "mkdir",
[SYS_close]   "close",
[SYS_trace]   "trace",
};

4.测试结果

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

./grade-lab-syscall trace

将你当前的修改提交到你的版本控制系统中,以保存当前工作的进度。

git commit -m "实现并通过了 trace 系统调用的所有测试"

三、System call sysinfo

1.实验要求

        在这个作业中,您将添加一个系统调用sysinfo,它收集有关正在运行的系统的信息。系统调用采用一个参数:一个指向struct sysinfo的指针(参见kernel/sysinfo.h)。内核应该填写这个结构的字段:freemem字段应该设置为空闲内存的字节数,nproc字段应该设置为state字段不为UNUSED的进程数。我们提供了一个测试程序sysinfotest;如果输出“sysinfotest: OK”则通过。

2.提示

  • MakefileUPROGS中添加$U/_sysinfotest
  • 当运行make qemu时,user/sysinfotest.c将会编译失败,遵循和上一个作业一样的步骤添加sysinfo系统调用。要在user/user.h中声明sysinfo()的原型,需要预先声明struct sysinfo的存在:

3.sysinfo实现

(1)在Makefile的UPROGS中添加$U/_sysinfotest

(2)在kernel/syscall.h中添加宏定义

#define SYS_sysinfo  23

(3)在user/usys.pl中添加entry("sys_info")

(4)在user/user.h中新增sysinfo结构体和sysinfo函数声明

(5)在kernel/syscall.c中添加sysinfo函数定义

(6)在kernel/syscall.c中添加sysinfo数组以及syscall_names中添加

(7)在kernel/kalloc.c中添加一个函数用于获取空闲内存量

uint64
getfreemem(void)
{
  struct run *r;
  uint64 ret = 0;
  acquire(&kmem.lock);
  r = kmem.freelist;
  while(r)
  {
    ret += PGSIZE;
    r = r->next;
  }
  release(&kmem.lock);
  return ret;
}

(8)在kernel/proc.c中添加一个函数获取进程数

遍历proc数组,统计处于活动状态的进程即可,循环的写法参考scheduler函数

uint64
getnproc(void)
{
  struct proc *p;
  uint64 ret = 0;
  for (p = proc; p < &proc[NPROC];p++)
  {
    if(p->state!=UNUSED)
    {
      ret++;
    }
  }
  return ret;
}

(9)实现sys_sysinfo,将数据写入结构体并传递到用户空间

uint64 sys_sysinfo(void)
{
  uint64 addr;
  if(argaddr(0,&addr)<0)
  {
    return -1;
  }

  struct sysinfo si;
  struct proc *p = myproc();
  si.freemem = getfreemem();
  si.nproc = getnproc();
  
  if(copyout(p->pagetable,addr,(char*)&si,sizeof(si))<0)
  {
    return -1;
  }

  return 0;
}

4.测试结果

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

./grade-lab-syscall sysinfo

将代码先保存在本地

推送到远程仓库

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring Cloud Alibaba之负载均衡组件Ribbon

一、什么是负载均衡&#xff1f; &#xff08;1&#xff09;概念&#xff1a; 在基于微服务架构开发的系统里&#xff0c;为了能够提升系统应对高并发的能力&#xff0c;开发人员通常会把具有相同业务功能的模块同时部署到多台的服务器中&#xff0c;并把访问业务功能的请求均…

grpc学习golang版( 五、多proto文件示例 )

系列文章目录 第一章 grpc基本概念与安装 第二章 grpc入门示例 第三章 proto文件数据类型 第四章 多服务示例 第五章 多proto文件示例 第六章 服务器流式传输 第七章 客户端流式传输 第八章 双向流示例 文章目录 一、前言二、定义proto文件2.1 公共proto文件2.2 语音唤醒proto文…

简单多状态DP问题

这里写目录标题 什么是多状态DP解决多状态DP问题应该怎么做&#xff1f;关于多状态DP问题的几道题1.按摩师2.打家劫舍Ⅱ3.删除并获得点数4.粉刷房子5.买卖股票的最佳时期含手冷冻期 总结 什么是多状态DP 多状态动态规划&#xff08;Multi-State Dynamic Programming, Multi-St…

Elasticsearch 第四期:搜索和过滤

序 2024年4月&#xff0c;小组计算建设标签平台&#xff0c;使用ES等工具建了一个demo&#xff0c;由于领导变动关系&#xff0c;项目基本夭折。其实这两年也陆陆续续接触和使用过ES&#xff0c;两年前也看过ES的官网&#xff0c;当时刚毕业半年多&#xff0c;由于历史局限性导…

服务器raid5坏盘-换盘-修复阵列过程

目录 背景原因分析解决步骤名词解释进入raid管理界面换回旧4号&#xff0c;进行import再次更换4号盘 总结 背景 服务器除尘之后文件服务器部分文件不能访问了,部分文件夹内容为空&#xff0c;起初以为是新配置的权限的问题&#xff0c;排查之后发现不仅仅是权限问题 jumpserv…

基于Java的会员制医疗预约服务管理信息系统

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;Java技术ssm框架&#xff0c;结合JSPM工作流引擎 工具&#xff1a;IDEA/Eclipse、Navicat、Maven …

ORA-01775: 同义词的循环链问题

一、问题描述 ORA-01775: 同义词的循环链问题 二、 原因分析 同义词对应的对象&#xff08;表等&#xff09;已删除&#xff0c;不存在了。 可能原因&#xff1a; 删除数据库对象&#xff0c;但是忘记删除同义词。删除一个用户&#xff0c;但忘记删除此用户中相关的同义词…

C语言—自定义类型:联合和枚举

1.联合体 1.1联合体类型的声明 像结构体一样&#xff0c;联合体也是由一个或者多个成员构成&#xff0c;这些成员可以是不同的类型。 但是编译器只为最大的成员分配足够的内存空间。联合体的特点是所有成员共用同一块内存空间。所以联合体也叫&#xff1a;共用体。 不难发现…

前端优化:首屏加载速度的实践

目录 目录 前言 多图片的懒加载 避免用户多次点击请求 骨架屏原理 结束语 前言 随着互联网技术的飞速发展&#xff0c;前端网页逐渐取代了传统客户端成为用户获取信息、进行交互的重要渠道&#xff0c;但是网页也有常见的弊端&#xff0c;比如网页首屏加载速度的快慢直接…

Apple - Text Layout Programming Guide

本文翻译整理自&#xff1a;Text Layout Programming Guide&#xff08;更新日期&#xff1a;2014-02-11 https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/TextLayout/TextLayout.html#//apple_ref/doc/uid/10000158i 文章目录 一、文本布局编程指…

使用ioDraw,AI绘图只需几秒钟!

只需几秒钟&#xff0c;就能将文字或图片转化为精准的思维导图、流程图、折线图、柱状图、饼图等各种图表&#xff01; 思维导图 思维导图工具使用入口 文字转思维导图 将文本大纲或想法转换成可视化的思维导图&#xff0c;以组织和结构化您的想法。 图片转思维导图 从现有…

一加12搞机(kernelsu+lsposed)

刷机 温馨提示&#xff1a;如果你不知道root的意义在哪&#xff0c;建议不要解锁和root&#xff0c;到时候救砖或者回锁都挺麻烦。 刷全量包 最新版的系统没有更新推送&#xff0c;所以去一加社区[0]找了个全量包来刷&#xff0c;。安装方式可以看帖子里的内容&#xff0c;说…

XML简介XML 使用教程XML的基本结构XML的使用场景

学习总结 1、掌握 JAVA入门到进阶知识(持续写作中……&#xff09; 2、学会Oracle数据库入门到入土用法(创作中……&#xff09; 3、手把手教你开发炫酷的vbs脚本制作(完善中……&#xff09; 4、牛逼哄哄的 IDEA编程利器技巧(编写中……&#xff09; 5、面经吐血整理的 面试技…

RocketMQ实战:一键在docker中搭建rocketmq和doshboard环境

在本篇博客中&#xff0c;我们将详细介绍如何在 Docker 环境中一键部署 RocketMQ 和其 Dashboard。这个过程基于一个预配置的 Docker Compose 文件&#xff0c;使得部署变得简单高效。 项目介绍 该项目提供了一套 Docker Compose 配置&#xff0c;用于快速部署 RocketMQ 及其…

【图像超分辨率】一个简单的总结

文章目录 图像超分辨率(Image Super-Resolution, ISR)1 什么是图像超分辨率&#xff1f;2 图像超分辨率通常有哪些方法&#xff1f;&#xff08;1&#xff09;基于插值的方法&#xff08;2&#xff09;基于重建的方法&#xff08;3&#xff09;基于学习的方法&#xff08;LR im…

新工具:轻松可视化基因组,部分功能超IGV~

本次分享一个Python基因组数据可视化工具figeno。 figeno擅长可视化三代long reads、跨区域基因组断点视图&#xff08;multi-regions across genomic breakpoints&#xff09;、表观组数据&#xff08;HiC、ATAC-seq和ChIP-seq等&#xff09;可视化、WGS中的CNV和SV可视化等。…

VRay是什么?有什么特点?渲染100邀请码1a12

Vray是由Chaos Group开发的高性能渲染引擎&#xff0c;能为不同的三维建模软件提供图像和动画渲染服务&#xff0c;它有以下几个特点。 1、Vray采用了先进的光线追踪技术&#xff0c;能够模拟真实世界中光线的传播和反射&#xff0c;生成的图像和动画十分逼真。 2、Vray提供了…

通俗范畴论4 范畴的定义

注:由于CSDN无法显示本文章源文件的公式,因此部分下标、字母花体、箭头表示可能会不正常,请读者谅解 范畴的正式定义 上一节我们在没有引入范畴这个数学概念的情况下,直接体验了一个“苹果1”范畴,建立了一个对范畴的直观。本节我们正式学习范畴的定义和基本性质。 一个…

web刷题记录(7)

[HDCTF 2023]SearchMaster 打开环境&#xff0c;首先的提示信息就是告诉我们&#xff0c;可以用post传参的方式来传入参数data 首先考虑的还是rce&#xff0c;但是这里发现&#xff0c;不管输入那种命令&#xff0c;它都会直接显示在中间的那一小行里面&#xff0c;而实际的命令…

centos 破解密码

重启您的CentOS系统。 在GRUB引导加载器启动过程中&#xff0c;当看到启动画面时&#xff0c;按下e键进入编辑模式。 找到以 linux16 或 linux 开头的启动行。 在该行的末尾添加 rd.break 或者ro&#xff08;只读&#xff09;修改为 rw 加init/sysroot/bin/sh参数&#xff0…