xv6---Lab2: system calls

news2024/11/24 8:43:56

目录

参考资料:

2.1 抽象物理资源

2.2 特权模式与系统调用

2.3 内核的组织

2.5 进程概览

2.6 Code: 启动xv6,第一个进程和系统调用

4.2 Trap from user space

System call tracing 

关于syscall函数的代码

每个syscall是由usys.pl自动生成为usys.S

trace代码实现

系统调用流程

系统调用流程小结:

Sysinfo

实验目标

预备知识:

代码实现:syscall add sysinfotest func · zion6135/xv6@0b1cdcc · GitHub


参考资料:

  • Lab: System calls
  • xv6-book翻译(自用)第二章 - 知乎
  • 阅读xv6 book章节2、4.3、4.4

2.1 抽象物理资源

  • 系统调用接口经过精心设计,既为程序员提供了方便,又提供了强大隔离的可能性。**Unix接口不是抽象资源的唯一方法,但它已被证明是一种非常好的方法。**
  • CPU为强隔离提供硬件支持。例如,RISC-V有三种CPU可以执行指令的模式:Machine mode、Supervior mode和User mode。 在机器模式下执行的指令具有完全权限;CPU以机器模式启动。机器模式主要用于配置计算机。xv6在Machine mode下执行几行,然后更改为Supervior mode。

2.2 特权模式与系统调用

  • 应用程序只能执行User mode指令,并被称为在用户空间中运行,而处于Supervior mode的软件可以执行特权指令,并被称为在内核空间中运行
  • 在Supervior mode中,CPU可以执行特权指令:例如,启用和禁用中断,读取和写入保存页表地址的寄存器等。如果User mode的应用程序尝试执行特权指令,则CPU不会执行该指令,但是会切换到Supervior mode,以便Supervior mode的代码可以终止应用程序。

2.3 内核的组织

  • 为了降低内核出错的风险,操作系统设计人员可以最小化在Supervior mode下运行的操作系统代码量,并在User mode下执行大部分操作系统代码。这种内核组织称为微内核

2.5 进程概览

  • xv6使用页表(硬件实现)来为每个进程提供其独有的地址空间。页表将虚拟地址映射为物理地址。xv6为每个进程维护不同的页表,一片地址空间包含了从虚拟地址0开始的用户内存。它的地址最低处放置进程的指令,接下来则是全局变量,栈区,以及一个用户可按需拓展的“堆”区。有许多因素限制了进程地址空间的最大大小,在Xv6这个值被定义为MAXVA

  • xv6内核为每个进程维护许多状态,并将其收集到struct proc中(kernel/proc.h:86)
  • 系统在进程之间切换实际上就是挂起当前运行的线程,恢复另一个进程的线程。线程的大多数状态(局部变量和函数调用的返回地址)都保存在线程的栈上。每个进程都有用户栈和内核栈(p->kstack)。
  • 在xv6中,一个进程由一个地址空间和一个线程组成。在实际的操作系统中,一个进程可能有多个线程来利用多个cpu。

2.6 Code: 启动xv6,第一个进程和系统调用

。。。

有三种事件会导致CPU不按照原先的执行顺序执行:系统调用(ecall)、异常、硬件中断。

4.2 Trap from user space

uservec(trampoline.S) -> usertrap(trap.c) -> usertrapret(trap.c) -> userret(trampoline.S)

System call tracing 

  • 参考

xv6-lab2-syscall_Wound+=s的博客-CSDN博客

  • 实现一个系统调用的跟踪

例如,要跟踪fork系统调用,程序调用trace(1 << SYS_fork),其中SYS_fork是kernel/syscall.h中的一个系统调用号 [ #define SYS_fork    1 ]

  • 切到分支syscall

$ git fetch

$ git checkout syscall

$ make clean

  • MIT6.s081-2020 操作系统入门 Lab2 System Calls - 知乎

关于syscall函数的代码

  •  以kill.c的系统调用为例:调用了syscall函数 kill() ,syscall定义在/user/user.h文件下。

每个syscall是由usys.pl自动生成为usys.S

  • Makefile的编译如下:usys.pl通过perl工具生成usys.S
$U/usys.S : $U/usys.pl
    perl $U/usys.pl > $U/usys.S
  • 做了些省略,以open函数为例
#!/usr/bin/perl -w

# Generate usys.S, the stubs for syscalls.

print "# generated by usys.pl - do not edit\n";

print "#include \"kernel/syscall.h\"\n";

sub entry {
    my $name = shift;
    print ".global $name\n";
    print "${name}:\n";
    print " li a7, SYS_${name}\n";
    print " ecall\n";
    print " ret\n";
}
	

entry("open");

  • 生成的usys.S如下:以open函数为例
# generated by usys.pl - do not edit
#include "kernel/syscall.h"
.global open
open:
 li a7, SYS_open
 ecall
 ret
  • trace代码实现

finish trace syscall funnction · zion6135/xv6@cbf80c3 · GitHub

  • 系统调用流程

用户空间:

  • 执行trace 32 grep hello README   

user/trace.c里执行grep hello README 并且执行trace 32, 而trace和grep都算系统调用。

上述usys.pl通过perl生成usys.S代码,可在其中找到trace和grep的汇编实现。以trace为例

.global trace
trace:
 li a7, SYS_trace  #等价于 li a7 22, 表示将22这个数字加载到寄存器a7
 ecall   #系统调用
 ret

而执行trace系统调用(li a7 22 和 ecall之后,),程序会跳转到syscall.c的void syscall(void)函数,这里就可理解为什么要把a7赋值为22了。

执行syscall函数的时候,会将num赋值为寄存器a7的值,并通过num找到syscalls中的系统调用号对应的函数指针。从而去执行sys_trace函数!!并将执行结果赋值给寄存器a0

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]();
  } else {
    printf("%d %s: unknown sys call %d\n",
            p->pid, p->name, num);
    p->trapframe->a0 = -1;
  }
}

至此可以去调用sys_trace函数了,这里需要关注:trace如何拿到传入的参数。可见argint(0. &n); 可将传入的int参数从寄存器p->trapframe->a0中拿到。

uint64
sys_trace(void)
{
  int n;
  //拿到trace传递的第一个参数,到变量n
  if(argint(0, &n) < 0)
    return -1;
  myproc()->trace_mask = n;
  return 0;
}

具体实现如下:argint用于拿到系统调用传递的参数,0-5的参数,对应寄存器a0-a5

// Fetch the nth 32-bit system call argument.
int argint(int n, int *ip) {
  *ip = argraw(n);
  return 0;
}
​
static uint64 argraw(int n) {
  struct proc *p = myproc();
  switch (n) {
  case 0:
    return p->trapframe->a0;
  case 1:
    return p->trapframe->a1;
  case 2:
    return p->trapframe->a2;
  case 3:
    return p->trapframe->a3;
  case 4:
    return p->trapframe->a4;
  case 5:
    return p->trapframe->a5;
  }
  panic("argraw");
  return -1;
}
  • MIT-6.S081-2020实验(xv6-riscv64)二:syscall - YuanZiming - 博客园
  • 在用户空间:通过系统调用进入内核,需在trampoline.S文件去执行uservec函数:保存了上下文,执行trap.c文件的usertrap()函数---》执行到syscall.c的syscall()函数---》在根据syscall函数的具体实现的sys_xxx函数。

  • 系统调用流程小结:

  1. 用户空间:trace调用 
  2. (由perl生成的汇编函数:trace) 调用ecall +调用号(SYS_trace)存入寄存器a7 
  3. 进入trampline.S【通过ecall从用户态陷入内核态】的调用syscall()函数(syscall.c)
  4. 这里会去拿到寄存器a7的数据,并根据 a7去调用调用号对应的系统函数
  5. 执行系统调用的真正实现sys_trace

Sysinfo

实验目标

  • 实现一个系统调用sysinfo (struct sysinfo*)

  • struct sysinfo {

      uint64 freemem;   // 剩余可用的内存大小bytes

      uint64 nproc;     // 记录进程状态 != UNUSED的进程

    };

  • 本例子有一个测试程序user/sysinfotest.c ===>sysinfotest  打印sysinfotest ok即代表测试通过

预备知识:

  • kalloc.c数据结构
内核程序后的第一个地址, 由kernel.ld定义
extern char end[]; // first address after kernel.
                   // defined by kernel.ld.

struct run {
  struct run *next; 将内存分为一块一块的,直到没有内存为止
};

struct {
  struct spinlock lock;
  struct run *freelist;   指向可用内存的链表
} kmem;
  • kinit:初始化spinlock, 和将kernel后的第一个地址开始,全部整理到freelist链表。
void
kinit()
{
  initlock(&kmem.lock, "kmem");  // 初始化spinlock
  freerange(end, (void*)PHYSTOP); 初始化 从end到PHYSTOP的物理内存
}
  • kalloc:从freelist链表取PGSIZE大小的内存去使用。并更新freelist指向的链表头。
  • kfree:添加PGSIZE大小的数据到链表freelist。
  • freerange:从pa_start到pa_end以PGSIZE大小为单位作为链表节点添加到freelist。

每一页的大小为PGSIZE (#define PGSIZE 4096 // bytes per page)  大小为4K

void
freerange(void *pa_start, void *pa_end)
{
  char *p;
  p = (char*)PGROUNDUP((uint64)pa_start);
  for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE)
    kfree(p);
}

注:kalloc.c操作的是直接的物理地址。并不等同于我们在系统之上分配的内存。

  • 系统之上的内存分配叫虚拟内存,通过内核的MMU单元去管理并映射到物理内存。这是额外的话题。

参考:Chapter 3: Page Tables - 知乎

代码实现:syscall add sysinfotest func · zion6135/xv6@0b1cdcc · GitHub

  1. 实现sysinfo的系统调用函数接口,先return 0
  2. 在kalloc.c中添加获取可用内存大小
  3. 在proc中去遍历proc[NPROC]得到所有的UBUSED的进程,从而可以得到可用进程数
  4. sysinfo调用接口获取到struct sysinfo的内容
  5. 从内核拷贝数据到userspace

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

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

相关文章

SAP 异常现象之同一个IDoc可以被POST两次触发2张不同的物料凭证

SAP 异常现象之同一个IDoc可以被POST两次触发2张不同的物料凭证 玩过SAP IDoc的童鞋都知道&#xff0c;一个IDoc正常情况下是只能被POST一次的&#xff0c;不可以POST两次的。 比如如下的IDoc 0000000205423126已经被POST了&#xff0c;其状态为53&#xff0c; Material Docume…

2-FreeRTOS编码标准、风格指南

1- 编码标准 FreeRTOS源文件(对所有端口通用&#xff0c;但对端口层不通用)符合MISRA编码标准指南。使用pc-lint和链接lint配置文件检查遵从性。由于标准有很多页长&#xff0c;并且可以从MISRA处以非常低的费用购买&#xff0c;所以我们在这里没有复制所有的规则。 就是下面这…

时间序列:时间序列模型---白噪声

本文是Quantitative Methods and Analysis: Pairs Trading此书的读书笔记。 白噪声&#xff08;white noise)是最简单的随机时间序列&#xff08;stochastic time series)。 在每一时刻&#xff0c;从一个正态分布中抽取一个值从而形成白噪声时间序列。并且&#xff0c;这个正…

1. 关于pytorch中的数据操作的广播机制

在某些情况下&#xff0c;即使形状不同&#xff0c;我们仍然可以通过调用 广播机制&#xff08;broadcasting mechanism&#xff09;来执行按元素操作。 这种机制的工作方式如下&#xff1a;首先&#xff0c;通过适当复制元素来扩展一个或两个数组&#xff0c; 以便在转换之后&…

第七章 贝叶斯分类器(下)

7.5 贝叶斯网 贝叶斯网亦称“信念网”&#xff0c;它借助有向无环图&#xff08;DAG&#xff09;来刻画属性之间的依赖关系&#xff0c;并使用条件概率表&#xff08;CPT&#xff09;来描述属性的联合概率分布。 具体来说&#xff0c;一个贝叶斯网B由结果G和参数Θ两部分构成&…

安全漏洞分类之CNNVD漏洞分类指南

适用范围说明 凡是被国家信息安全漏洞库&#xff08;CNNVD&#xff09;收录的漏洞&#xff0c;均适用此分类规范&#xff0c;包括采集的公开漏洞以及收录的未公开漏洞&#xff0c;通用型漏洞及事件型漏洞。 漏洞类型 CNNVD将信息安全漏洞划分为26种类型&#xff0c;分别是&…

基于Tree-LSTM网络语义表示模型

TC&#xff1b;DR 目前的LSTM仅能对序列信息进行建模&#xff0c; 但是自然语言中通常由词组成的短语形成了句法依存的语义树。为了学习到树结构的语义信息。论文中提出了两种Tree-LSTM模型。Child-Sum、Tree-LSTM、和N-ary Tree LSTMs。实验部分的Tree-LSTM、对比多种LSTMs的…

09【MyBatis多表关联查询】

文章目录三、MyBatis多表关联查询3.1 表的关系3.2 一对一查询3.2.1 搭建环境3.2.2 需求分析3.2.3 dao接口3.2.3 mapper.xml3.2.4 测试3.2.5 配置MyBatis一对一关系1&#xff09;传统映射&#xff1a;2&#xff09;使用association标签映射3.3 一对多查询3.3.1 需求分析3.3.2 da…

Codeforces Round #799 (Div. 4) H. Gambling

翻译&#xff1a; 玛丽安在赌场。赌场里的游戏是这样的。 在每一轮之前&#xff0c;玩家在1到109之间选择一个数字。在那之后&#xff0c;一个有109个面的骰子被滚动&#xff0c;这样就会出现1到109之间的随机数。如果玩家猜对了数字&#xff0c;他们的钱就会翻倍&#xff0c…

C++原子操作和互斥锁性能(速度)对比

先说结论&#xff1a;原子操作性能&#xff08;速度&#xff09;强于互斥锁&#xff0c;下面用例子进行说明。 编写测试demo&#xff0c;开启两个线程&#xff0c;对全局变量n分别进行自增、自减操作&#xff0c;计算执行时间。 首先看没有用任何手段进行互斥的情况&#xff0c…

Springboot生成Word/EXECL/PPTX文档

目录 一、概述 二、使用介绍 第一种Poi-tl&#xff1a; 1、介绍 2、功能 第二种Poi: 什么是POI 二进制分布 源码分发 一、概述 Word模板引擎&#xff1a;使用Word模板和数据生成对应的Word文档。 方案移植性功能性易用性 Poi-tl Java跨平台 Word模板引擎&#…

【uniapp小程序】路由跳转navigator传参封装

文章目录&#x1f34d;前言&#x1f34b;正文1、看官网1.1 navigator API 介绍1.2、路由跳转参数传递1.3、五种常见的跳转方式1.3.1 uni.navigateTo(OBJECT)1.3.2 uni.redirectTo(OBJECT)1.3.3 uni.reLaunch(OBJECT)1.3.4 uni.switchTab(OBJECT)1.3.5 uni.navigateBack(OBJECT)…

Ensp用windows回环口连接cloud配置

Ensp模拟通过本机&#xff08;windows&#xff09;用python脚本批量配置华为数通设备时&#xff0c;为了避免对内网资源的浪费最好用回环口&#xff08;loopback&#xff09;。 一、windows开启loopback虚拟接口 概要&#xff1a; right click on window start menu icon an…

【C++】类型转换

目录 一、C语言风格类型转换 二、C风格类型转换 1.static_case 2.reinterpret_case 3、const_case 4、dynamic_case 三、RTTI 总结 一、C语言风格类型转换 在C语言中&#xff0c;如果赋值运算符左右两侧类型不同&#xff0c;或者形参与实参类型不匹配&#xff0c;或者返…

N-Gram模型介绍

N-gram是一种基于统计语言模型的算法&#xff0c;基本思想是将文本内容按照字节进行大小为N的滑动窗口操作&#xff0c;形成了长度是N的字节片段序列。 每一个字节片段称为gram&#xff0c;对所有gram的出现频度进行统计&#xff0c;并且按照事先设定好的阈值进行过滤&#xf…

PowerQuery:使用正则表达式

博客来源于我的语雀专栏&#xff1a;R 语言 语雀 更多内容同步更新请关注我的语雀&#xff1a;令平子 语雀 参考资料&#xff1a;部分已引用到各段落 在Power Query中使用正则表达式 Power query 自定义正则表达式函数 新建空白查询&#xff0c;粘贴以下代码&#xff0c;使用…

gcc: error trying to exec ‘cc1plus‘: execvp: no such file or directory

该问题是缺少gcc文件&#xff0c;或者gcc与g版本不匹配问题 问题来源1&#xff1a;系统文件的缺失或者不匹配 按照如下方法测试 安装主要是利用apt-get安装&#xff0c;如果没有root权限的话&#xff0c;只能下载源码进行编译安装&#xff0c;然后添加路径环境&#xff0c;安…

JavaWeb简单实例——Ajax请求

简单介绍&#xff1a; 在上一章节我们展示了关于jQuery的一些基本操作&#xff0c;接下来我们就要进行Ajax的一些基础操作&#xff0c;在真正执行操作之前&#xff0c;我们还需要一点前置的准备&#xff0c;就是关于发送和请求JSON数据的准备。 请求JSON数据&#xff1a; JS…

板卡测评 | 基于TI AM5708开发板——ARM+DSP多核异构开发案例分享

本次测评板卡是创龙科技旗下的TL570x-EVM,它是一款基于TI Sitara系列AM5708ARM Cortex-A15+浮点DSPC66x处理器设计的异构多核SOC评估板,由核心板和评估底板组成。核心板经过专业的PCB Layout和高低温测试验证,稳定可靠,可满足各种工业应用环境。 评估板接口资源丰富,引出…

你还不懂《顺序表》?那就不要错过这篇文章!!!

&#x1f387;&#x1f387;&#x1f387;作者&#xff1a; 小鱼不会骑车 &#x1f386;&#x1f386;&#x1f386;专栏&#xff1a; 《java练级之旅》 &#x1f393;&#x1f393;&#x1f393;个人简介&#xff1a; 一名专科大一在读的小比特&#xff0c;努力学习编程是我…