xv6---Lab2: system calls
创始人
2024-03-01 22:15:58
0

目录

参考资料:

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.plperl $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_openecallret
  • 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这个数字加载到寄存器a7ecall   #系统调用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传递的第一个参数,到变量nif(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");  // 初始化spinlockfreerange(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

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
有效的括号 一、题目 给定一个只包括 '(',')','{','}'...
【Ctfer训练计划】——(三... 作者名:Demo不是emo  主页面链接:主页传送门 创作初心ÿ...