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

news2025/6/18 17:50:53

文章目录

    • 一、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;
}

实验结果

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

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

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

相关文章

Selenium教程11:模拟账号密码,自动登入qq空间

Python爬虫教程30&#xff1a;Selenium网页元素&#xff0c;定位的8种方法&#xff01; Selenium自动化教程02&#xff1a;浏览器options配置及常用的操作方法 Selenium自动化教程03&#xff1a;延时等待的3种方式 Selenium自动化教程04&#xff1a;鼠标键盘网页的模拟操作 …

文件的相关概念及用法

文件的作用 程序运行时产生的数据都属于临时文件&#xff0c;程序一旦运行结束就会被释放。若想让数据保存下来&#xff0c;则可以通过文件将数据持久化 文件需要包含的头文件<fstream>(文件流) 文件分类 文件分为文本文件和二进制文件。 文本文件&#xff1a;文件以…

快速上手Git

目录 一、Git概述 二、Git的常用命令 Git全局配置 获取Git仓库 基本概念 本地仓库操作 远程仓库操作 分支操作 标签操作 三、在IDEA中使用Git 在IDEA中配置Git 本地仓库操作 远程仓库操作 分支操作 冲突解决 一、Git概述 Git是一个分布式版本控制工具&…

探索设计模式的魅力:深入了解适配器模式-优雅地解决接口不匹配问题

设计模式专栏&#xff1a;http://t.csdnimg.cn/nolNS 目录 一、引言 1. 概述 2. 为什么需要适配器模式 3. 本文的目的和结构 二、简价 1. 适配器模式的定义和特点 定义 特点 2. 适配器模式的作用和适用场景 作用 适用场景 3. 适配器模式与其他设计模式的比较 三、适配…

DolphinScheduler + Amazon EMR Serverless 的集成实践

01 背景 Apache DolphinScheduler 是一个分布式的可视化 DAG 工作流任务调度开源系统&#xff0c;具有简单易用、高可靠、高扩展性、⽀持丰富的使用场景、提供多租户模式等特性。适用于企业级场景&#xff0c;提供了一个可视化操作任务、工作流和全生命周期数据处理过程的解决方…

代码随想录算法刷题训练营day18

代码随想录算法刷题训练营day18&#xff1a;LeetCode(257)二叉树的所有路径、LeetCode(404)左叶子之和 LeetCode(257)二叉树的所有路径 题目 代码 import java.util.ArrayList; import java.util.List;/*** Definition for a binary tree node.* public class TreeNode {* …

媒体邀约:怎么吸引总体目标受众?

新闻媒体影响力日益扩大。不论是公司、机构还是其他&#xff0c;都希望能够通过新闻媒体的曝光来吸引更多总体目标受众。要想真正吸引住总体目标受众并非易事&#xff0c;需要一定的方案和方法。下面我们就深入探究媒体邀约推广的真相&#xff0c;共享怎么吸引总体目标受众的方…

挂耳耳机哪个牌子好?挂耳耳机产品的几大推荐

如果你也是个运动爱好者&#xff0c;那你一定知道边运动边听歌是多么的提神! 我试过好多种耳机&#xff0c;那些长长的线总在运动时不断甩来甩去&#xff0c;真是让人烦不胜烦。而且戴久了耳朵里也不舒服。所以找一款真正适合运动的挂耳耳机太重要了&#xff0c;它能让你的运动…

【Docker】docker Overlay2 文件系统原理

概述 overlayFS是被称为联合文件系统的其中一个解决方案。在2014年&#xff0c;发布了第一个版本并且合并到了Linux的内核3.18版本中&#xff0c;此时&#xff0c;在docker被称为是overlay文件驱动。后来在Linux 内核4.0 版本中进行了改进&#xff0c;称为overlay2。&#xff…

模拟堆

import java.util.Scanner;public class Main{static int N 100010, size,m;static int[] h new int[N]; //h[i]表示下标i这个点是第多少static int[] hp new int[N]; //hp[i]表示下标为i的节点是第几个被加进来的static int[] ph new int[N]; //ph[i]表示第i个加进来…

BIO、NIO变成与直接内存、零拷贝

一、网络通信 1、什么是socket&#xff1f; Socket 是应用层与 TCP/IP 协议族通信的中间软件抽象层&#xff0c;它是一组接口&#xff0c;一般由操作 系统提供。客户端连接上一个服务端&#xff0c;就会在客户端中产生一个 socket 接口实例&#xff0c;服务端每接受 一个客户端…

qt初入门7:进度条,定时器,时间控件练习

参考课本demo&#xff0c;空闲时间练习一下进度条&#xff0c;定时器&#xff0c;日期相关控件和使用。 1&#xff1a;demo运行结果 2&#xff1a;进度条控件梳理 进度条显示控件实际上是QProgressBar, 显示的进度可以通过代码控制&#xff0c;也可以通过其他控件上获取到的值…

Android SystemUI 介绍

目录 一、什么是SystemUI 二、SystemUI应用源码 三、学习 SystemUI 的核心组件 四、修改状态与导航栏测试 本篇文章&#xff0c;主要科普的是Android SystemUI &#xff0c; 下一篇文章我们将介绍如何把Android SystemUI 应用转成Android Studio 工程项目。 一、什么是Syst…

【K12】运用tk控件演示欧姆定律串联电阻小应用

上述代码是一个基于Python的图形用户界面&#xff08;GUI&#xff09;应用程序&#xff0c;用于演示欧姆定律。用户可以通过输入电阻值来计算电流&#xff0c;并在图形上显示结果。该程序使用了Tkinter库来创建GUI&#xff0c;matplotlib库来绘制图形&#xff0c;以及numpy库进…

使用git工具向GitHub远程仓库提交代码

注意更新仓库&#xff1a; 远程仓库更新方法&#xff1a; 本地仓库更新方法&#xff1a;在终端进入克隆的文件夹&#xff0c;依次执行如下指令&#xff1a; git config pull.rebase false //使用默认的合并策略 git pull //执行实际的拉取操作以下为常规操作&#xff…

【React教程】(3) React之表单、组件、事件处理详细代码示例

目录 事件处理示例1示例2示例3&#xff08;this 绑定问题&#xff09;示例4&#xff08;传递参数&#xff09;Class 和 Style 表单处理组件组件规则注意事项函数式组件&#xff08;无状态&#xff09;类方式组件&#xff08;有状态&#xff09;组件传值 Propsthis.props.childr…

bert提取词向量比较两文本相似度

使用 bert-base-chinese 预训练模型做词嵌入&#xff08;文本转向量&#xff09; 模型下载&#xff1a;bert预训练模型下载-CSDN博客 参考文章&#xff1a;使用bert提取词向量 下面这段代码是一个传入句子转为词向量的函数 from transformers import BertTokenizer, BertMod…

神经网络建立(结果可变)最小神经元

目录 介绍&#xff1a; 初始化&#xff1a; 建模: 预测&#xff1a; 改变结果&#xff1a; 介绍&#xff1a; 在深度学习中&#xff0c;神经元通常指的是人工神经元&#xff08;或感知器&#xff09;&#xff0c;它是深度神经网络中的基本单元。深度学习的神经元模拟了生…

腾讯发表多模态大模型最新综述,从26个主流大模型看多模态效果提升关键方法

在大规模语言模型&#xff08;LLMs&#xff09;通往通用人工智能&#xff08;AGI&#xff09;的道路中&#xff0c;从传统的单一的“语言模态”扩展到“图像”、“语音”等等的“多模态”必然是大模型进化的必经之路。 在过去的 2023 年&#xff0c;多模态大规模语言模型&…