6.s081 学习实验记录(三)system calls

文章目录

    • 一、use gdb
    • 二、syscall:trace
      • 注意:
      • 实验代码:
      • 实验结果:
    • 三、sysinfo
      • tips:
      • 实验代码
      • 实验结果

  • 需要切换到 syscall 分支

一、use gdb

学习使用 gdb 调试

  • make qemu-gdb
  • 打开一个新的终端: gdb-multiarch -x .gdbinit
    qemu 界面:
    在这里插入图片描述
    gdb 界面:
    在这里插入图片描述
  • gdb 界面: b syscall
  • gdb 界面:c
  • qemu 界面输入:ls(随便一个会发起系统调用的命令)
  • gdb界面:可以发现已经触发了断点,可以单步执行了
  • gdb界面:layout src,显示源码
    在这里插入图片描述
  • gdb界面:bt,打印调用栈
    在这里插入图片描述
  • 第一个问题询问什么函数触发了 syscall,从调用栈可知:usertrap,表示在用户态调用了系统调用API
  • 单步执行
    在这里插入图片描述
  • 打印当前进程 a7 寄存器的值:
    在这里插入图片描述
  • 由于a7寄存器用于保存系统调用号,可知用户态调用的是 SYS_read
  • gdb界面:p/x $sstatus 寄存器(值为0x22),表示之前特权级别为用户态(问题二)

二、syscall:trace

注意:

  • Makefile添加 $U/_trace
  • user/user.h 添加 trace 函数原型、
  • user/usys.pl 中添加系统调用入口
  • kernel/syscall.h 添加系统调用号
  • makefile 将执行 user/usys.pl,生成user/usys.S,里面存放的是系统调用的实际入口代码
  • kernel/sysproc.c 添加 sys_trace() 函数用于实现系统调用 trace的逻辑(函数逻辑:proc 中添加一个新的函数用于)
  • 修改 kernel/proc.c:fork(),拷贝父进程的 trace mask 到子进程
  • 修改 kernel/syscall.c:syscall() 用于打印
  • 准备一个 syscall 的名字的数组
  • 该系统调用逻辑其实比较简单,即 trace 命令本身是一个进程,接收的参数为另外一个命令,然后调用trace系统调用设置当前进程的 trace mask,然后 fork 子进程去执行参数中的命令,然后如果当前进程调用的所有系统调用在返回时,和掩码判断,如果为true,则打印
    • proc中需要添加一个 trace mask
    • 所有的系统调用返回需要增加一段和 trace mask 进行判断并打印的逻辑
    • 添加一个 trace 系统调用,用于设置当前进程的 trace mask,如果不设置,默认为0,即不 trace
    • fork的时候,需要把父进程的 trace mask 拷贝给子进程

实验代码:

  • 添加 trace nask:
  • kernel/proc.h
struct proc {
  // ....
  char name[16];               // Process name (debugging)
  uint64 trace_mask;           // 新增的记录当前进程的 trace 掩码
};
  • kernel/proc.c
int
fork(void)
{
  // ...其他代码
  np->sz = p->sz;
  np->trace_mask = p->trace_mask; // 子进程创建的时候拷贝父进程的trace mask
}
  • 添加 trace 系统调用:
  • makefile
UPROGS=\
	//其他代码
	$U/_trace\
  • user/user.h
struct stat;

// system calls
//其他代码
int trace(int);
//...
  • user/usys.pl
#!/usr/bin/perl -w
// 其他代码
entry("trace");
  • kernel/syscall.h
// ...
#define SYS_trace  22
  • kernel/syscall.c
// ...
// sys call name
static
char* syscalls_name[] = {
  [SYS_fork] = "syscall fork",
  [SYS_exit] = "syscall exit",
  [SYS_wait] = "syscall wait",
  [SYS_pipe] = "syscall pipe",
  [SYS_read] = "syscall read",
  [SYS_kill] = "syscall kill",
  [SYS_exec] = "syscall exec",
  [SYS_fstat] = "syscall fstat",
  [SYS_chdir] = "syscall chdir",
  [SYS_dup] = "syscall dup",
  [SYS_getpid] = "syscall getpid",
  [SYS_sbrk] = "syscall sbrk",
  [SYS_sleep] = "syscall sleep",
  [SYS_uptime] = "syscall uptime",
  [SYS_open] = "syscall open",
  [SYS_write] = "syscall write",
  [SYS_mknod] = "syscall mknod",
  [SYS_unlink] = "syscall unlink",
  [SYS_link] = "syscall link",
  [SYS_mkdir] = "syscall mkdir",
  [SYS_close] = "syscall close",
  [SYS_trace] = "syscall trace",
};

// ...
// 定义系统调用处理函数
extern uint64 sys_trace(void);

static uint64 (*syscalls[])(void) = {
[SYS_fork]    sys_fork,
// ....
// 定义系统调用实际处理函数
[SYS_trace]   sys_trace,
};

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

  num = p->trapframe->a7;
  if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
    // Use num to lookup the system call function for num, call it,
    // and store its return value in p->trapframe->a0
    p->trapframe->a0 = syscalls[num]();
    // 此处在每个进程的系统调用返回时,判断当前系统调用是否在该进程中被使能
    // 如果使能则进行打印
    if(p->trace_mask & num){
      printf("xxx:%s\n", syscalls_name[num]);
    }
  } else {
    printf("%d %s: unknown sys call %d\n",
            p->pid, p->name, num);
    p->trapframe->a0 = -1;
  }
}
  • kernel/sysproc.c
//实际系统调用函数实现,逻辑很简单,设置当前进程的trace mask
uint64
sys_trace(void){
  //参考上面的系统调用函数实现,获取参数
  int mask;
  argint(0, &mask);
  myproc()->trace_mask = mask;
  return 0;
}

实验结果:

  • make grade

在这里插入图片描述

三、sysinfo

tips:

  • 添加 $U/_sysinfotest 到makefile
  • 添加 user/sysinfotest.c 文件
  • 实现 sys_info 系统调用,步骤同 sys_trace
  • 注意在 user/user.h 声明 int sysinfo(struct sysinfo *) 函数之前,先预先声明结构体: struct sysinfo;struct sysinfokernel/sysinfo.h 中定义)
  • 参考 sys_fstat() (kernel/sysfile.c) 和 filestat() (kernel/file.c) 学习从内核态将 struct sysinfo 拷贝到用户态 (用户态传入一个地址,内核态通过 copyout 函数拷贝到用户态地址)
  • 统计内存使用量:kernel/kalloc.c 中添加一个统计free内存使用量的函数
  • 统计进程数量:kernel/proc.c 中添加一个统计进程数目的函数
  • 因此,我们实现的内核态的系统调用函数 sys_sysinfo 接收一个地址参数,然后调用统计内存的函数和统计进程数量的函数,最后使用 copy_out 拷贝到用户态

实验代码

  • makefile
$U/_sysinfotest\
  • user/user.h
struct stat;
struct sysinfo; //添加sysinfo的预声明
// system calls
// ...
int trace(int);
int uptime(void);
int sysinfo(struct sysinfo *); //声明用户态系统调用API
  • user/usys.pl
entry("sysinfo");
  • kernel/syscall.h
#define SYS_sysinfo  23
  • kernel/syscall.c
static
char* syscalls_name[] = {
  // ....
  [SYS_trace] = "syscall trace",
  [SYS_sysinfo] = "syscall sysinfo",
};
// ....
extern uint64 sys_sysinfo(void);

static uint64 (*syscalls[])(void) = {
// ...
[SYS_trace]   sys_trace,
[SYS_sysinfo]   sys_sysinfo,
};
  • kernel/sysproc.c
#include "sysinfo.h"
// ....其他代码
uint64
sys_sysinfo(void){
  uint64 addr; // user pointer to struct sysinfo
  struct sysinfo sysinfo;
  argaddr(0, &addr);

  sysinfo.freemem = kfree_mem_get();
  sysinfo.nproc = get_proc_num();
  if(copyout(myproc()->pagetable, addr, (char *)&sysinfo, sizeof(sysinfo)) < 0){
    return -1;
  }
  return 0;
}
  • kernel/def.h
// proc.c
//....proc.c 中其他函数
uint64          get_proc_num(void); //获取进程数目

// kalloc.c
void*           kalloc(void);
void            kfree(void *);
void            kinit(void);
uint64          kfree_mem_get(void); //获取空闲内存
  • kernel/proc.c
uint64 get_proc_num(void){
  uint64 num = 0;
  struct proc* p;
  for(p = proc; p < &proc[NPROC]; p++) {
    if(p->state == UNUSED) {
      continue;
    } else {
      num++;
    }
  }
  return num;
}
  • kernel/kalloc.c
uint64 kfree_mem_get(void){
  uint64 free = 0;
  struct run *r = kmem.freelist;
  acquire(&kmem.lock);
  while(r){
    free += PGSIZE;
    r = r->next;
  }
  release(&kmem.lock);
  return free;
}

实验结果

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

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

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

相关文章

换个思路快速上手UML和plantUML——时序图

上一章我们介绍了类图&#xff0c;我们很清楚&#xff0c;类图是从更加宏观的角度去梳理系统结构的&#xff0c;从类图中我们可以获取到类与类之间&#xff1a;继承&#xff0c;实现等关系信息&#xff0c;是宏观逻辑。下面我们继续换一个思路&#xff1a;作为一名软件工程结构…

【周赛】第382场周赛

&#x1f525;博客主页&#xff1a; A_SHOWY&#x1f3a5;系列专栏&#xff1a;力扣刷题总结录 数据结构 云计算 数字图像处理 力扣每日一题_ 从这一场&#xff08;第382场周赛&#xff09;周赛开始记录&#xff0c;目标是尽快达到准确快速AC前三道题&#xff0c;每场比赛…

贝莱德里程碑

作者&#xff1a;秦晋 见证奇迹的时刻。 1月27日&#xff0c;全球资产管理巨头贝莱德在美国证监会于1月10日审批通过比特币ETF的17天内&#xff0c;其比特币现货ETF资产管理规模已经突破20亿美元。持有比特币总数已达49952个。领跑其他9只比特币ETF机构参与者。不包括灰度GBTC&…

【JavaScript 漫游】专栏介绍

专栏介绍 本专栏旨在记录 JavaScript 核心语法&#xff0c;作为笔者日常学习和工作中的参考手册和代码示例仓库。 内容上力求覆盖 ES5、DOM、BOM 和 ES6 规范的所有内容。对于常用且重要的知识点&#xff0c;应该详细描述并附带有大量的代码示例。对于在工作场景中很少用到的…

数据结构——用Java实现二分搜索树

目录 一、树 二、二分搜索树 1.二叉树 2.二分搜索树 三、代码实现 1.树的构建 2.获取树中结点的个数 3.添加元素 4.查找元素 &#xff08;1&#xff09;查找元素是否存在 &#xff08;2&#xff09;查找最小元素 &#xff08;3&#xff09;查找最大元素 5.二分搜索…

算法39:统计全 1 子矩形(力扣1504)----单调栈

题目: 给你一个 m x n 的二进制矩阵 mat &#xff0c;请你返回有多少个 子矩形 的元素全部都是 1 。 示例 1&#xff1a; 输入&#xff1a;mat [[1,0,1],[1,1,0],[1,1,0]] 输出&#xff1a;13 解释&#xff1a; 有 6 个 1x1 的矩形。 有 2 个 1x2 的矩形。 有 3 个 2x1 的矩…

(2024|ICLR,MAD,真实数据与合成数据,自吞噬循环)自消耗生成模型变得疯狂

Self-Consuming Generative Models Go MAD 公和众和号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 2. 自吞噬生成模型 2.1 自吞噬过程 2.2 自吞噬过程的变体 2.3 自吞噬循环中的偏…

Advanced EFS Data Recovery:恢复 Windows NTFS 中 EFS 加密文件

Advanced EFS Data Recovery 数据恢复软件可以破解 NTFS 加密&#xff0c;并解密受 Windows 加密文件系统 &#xff08;EFS&#xff09; 保护的文件。 Advanced EFS Data Recovery 功能列表 通过破解加密文件系统 &#xff08;EFS&#xff09; 来解除 NTFS 加密 解密转移到另…

Java面试题(11)

59.说一说springMVC的运行流程 1. 用户向服务器发送请求&#xff0c;请求被 Spring 前端控制 Servelt DispatcherServlet 捕获&#xff1b; 2. DispatcherServlet 对请求 URL 进行解析&#xff0c;得到请求资源标识符&#xff08;URI&#xff09;。然后根据该 URI&#xff0c;…

STM32 E18-D80NK红外避障传感器

E18-D80NK-N是一款红外光电传感器&#xff0c;它同时具备发射和接收功能。通过对发射光进行调制后发出&#xff0c;并通过接收头对反射光进行解调输出。 E18-D80NK-N采用了透镜来增强传感器的性能&#xff0c;使其能够检测更远的距离。根据红外光的特性&#xff0c;不同颜色的…

VitePress-04-文档中的表情符号的使用

说明 vitepress 的文档中是支持使用表情符号的&#xff0c;像 &#x1f602; 等常用的表情都是支持的。 本文就来介绍它的使用方式。 使用语法 语法 &#xff1a; :表情名称: 例如 &#xff1a; :joy: &#x1f602; 使用案例代码 # 体会【表情】的基本使用 > hello world …

22.Lambda 表达式

Lambda 表达式 1. 概况2. 函数式接口3. 格式3.1 完整格式3.2 省略格式 4. 代码示例5. 输出结果6. 注意事项 学习Lambda表达式之前最好先学会 匿名内部类 具体信息请查看 API 帮助文档 1. 概况 Lambda 表达式是一种在编程中用来表示匿名函数的简洁语法。它是基于函数式编程风格…

2024.1.27每日一题

LeetCode 最大合金数 2861. 最大合金数 - 力扣&#xff08;LeetCode&#xff09; 题目描述 假设你是一家合金制造公司的老板&#xff0c;你的公司使用多种金属来制造合金。现在共有 n 种不同类型的金属可以使用&#xff0c;并且你可以使用 k 台机器来制造合金。每台机器都需…

SpringBoot之JWT登录

JWT JSON Web Token&#xff08;JSON Web令牌&#xff09; 是一个开放标准(rfc7519)&#xff0c;它定义了一种紧凑的、自包含的方式&#xff0c;用于在各方之间以JSON对象安全地传输信息。此信息可以验证和信任&#xff0c;因为它是数字签名的。jwt可以使用秘密〈使用HNAC算法…

嵌入式学习第十三天

9.指针: &#xff08;1&#xff09;const指针 const 关键字 常量(只读) 1.const int *p; 2.int const *p; 1和2是等价的 const修饰 *p,指针变量p的值可以改变,但不能利用指针修改指向空间中的值 3.int *const p; const修饰 p,指针变量p的值不能改变…

【教程】极简Docker搭建“帕鲁幻兽PalWorld”服务器, 附资源

注意&#xff1a; 如果搭建在个人服务器或者内网中&#xff0c;需要做内网穿透&#xff0c;可以看这篇博客&#xff1a; 【教程】超详细安装和使用免费内网穿透软件Zerotier-One-CSDN博客文章浏览阅读523次&#xff0c;点赞8次&#xff0c;收藏8次。真的很详细https://blog.csd…

[设计模式Java实现附plantuml源码~结构型]树形结构的处理——组合模式

前言&#xff1a; 为什么之前写过Golang 版的设计模式&#xff0c;还在重新写Java 版&#xff1f; 答&#xff1a;因为对于我而言&#xff0c;当然也希望对正在学习的大伙有帮助。Java作为一门纯面向对象的语言&#xff0c;更适合用于学习设计模式。 为什么类图要附上uml 因为很…

Mysql查询数据

1 基本查询语句 MySQL从数据表中查询数据的基本语句为SELECT语句。SELECT语句的基本格式是&#xff1a; 2 单表查询 2.1 查询所有字段 SELECT * FROM 表名; 2.2 在SELECT语句中指定所有字段 SELECT f_id, s_id ,f_name, f_price FROM fruits; 2.3 查询单个字段 SELECT 列名FR…

Ajax入门与使用

目录 ◆ AJAX 概念和 axios 使用 什么是 AJAX&#xff1f; 怎么发送 AJAX 请求&#xff1f; 如何使用axios axios 函数的基本结构 axios 函数的使用场景 1 没有参数的情况 2 使用params参数传参的情况 3 使用data参数来处理请求体的数据 4 上传图片等二进制的情况…

C数据类型

目录 1. 数据类型分类 2. 整数类型 3. 浮点类型 4. void 类型 5. 类型转换 1. 数据类型分类 在 C 语言中&#xff0c;数据类型指的是用于声明不同类型的变量或函数的一个广泛的系统。变量的类型决定了变量存储占用的空间&#xff0c;以及如何解释存储的位模式。 C 中…