6.1810: Operating System Engineering <Lab2 syscall: System calls>

课程链接:6.1810 / Fall 2023

一、本节任务 

二、要点

操作系统要满足三要素:并发、隔离、交互(multiplexing, isolation, and interaction)。

宏内核(monolithic kernel:是操作系统核心架构的一种,此架构的特性是整个核心程序都是以核心空间(Kernel Space)的身份及监管者模式(Supervisor Mode)来运行。宏内核中各个部分通信十分容易,缺点就是一旦操作系统某个部分出问题,整个内核都可能直接崩溃。

为了减少内核中出现错误的风险,操作系统设计者减少在 Supervisor Mode 下运行的操作系统代码,并在用户模式运行大部分操作系统模块。这种内核组织被称为微内核(microkernel

IPC(inter-process communication):进程间通信

xv6使用硬件实现的页表(page table)来给每个进程提供自己的地址空间。riscv页表把虚拟地址(riscv指令操作的地址)转换成物理地址(cpu发送到主存储器的地址)。

xv6为每个进程的地址空间维护一个单独的地址空间,包括从虚拟地址零开始的进程的用户内存。首先是指令,然后是全局变量,然后是堆栈,最后是一个“堆”区域(对于malloc),进程可以根据需要进行扩展。

At the top of the address space xv6 reserves a page for a trampoline and a page mapping the process’s trapframe . Xv6 uses these two pages to transition into the kernel and back; the trampoline page contains the code to transition in and out of the kernel and mapping the  trapframe is necessary to save/restore the state of the user process.

在结构体 struct proc(kernel/proc.h)中保存了进程的各种状态。一个进程最重要的内核状态包括它的页表,它的内核栈,和它的运行状态。

每个进程有两个栈:用户栈和内核栈,当进程执行用户指令时,只会使用用户栈,此时内核栈是空的;当进程进入内核空间时(系统调用或中断),内核代码(如系统函数 sys_open())在进程的内核栈里面执行。当进程在内核态时,它的用户栈仍然包含之前保存的数据,内核栈是独立的,所以即使进程破坏了其用户堆栈,内核也可以执行。

在 riscv 中,进程可以通过 ecall 指令来进行系统调用,ecall 指令会提高硬件的特权级别,并且跳转到内核定义的入口点。入口点上的代码会切换到一个内核堆栈,并执行实现系统调用的内核代码。当系统调用完成,内核会切回用户栈并且通过 sret 指令返回用户空间,sret 指令降低硬件的特权级别,并且返回到用户进行系统调用的下一条指令继续执行。

总之,进程有两个设计思想:一个是地址空间,给每个进程都拥有自己的内存空间的错觉,另一个是线程,给每个进程都拥有自己的 CPU 的错觉。

xv6 如何启动

当机器上电,它会运行一个存储在只读内存中的引导程序(boot loader),引导程序会把 xv6 内核搬运到内存中,然后,在机器模式下,cpu 执行 xv6 的 _entry(kernel/entry.S),在开始时,riscv 会禁用分页硬件,虚拟地址直接映射到物理地址。

引导程序会把 xv6 内核搬运到内存物理地址 0x80000000 处,因为 0x0 到 0x80000000 之间包含 I/O 设备。

在 entry.S 中,会先初始化对应 cpu hart 的栈指针,然后跳转到 start(kernel/start.c)处执行。

在 start() 中,先将 mstatus 寄存器的 MPP(Previous Privilege mode)位设置成 Supervisor,然后将 mepc 寄存器设置为 main(kernel/main.c)函数的地址。这样的话在使用 mret 指令就可以将特权级别切换为 Supervisor,并且跳转到 main() 处执行。最后 start 中还会配置时钟中断,配置 machine-mode 的 mtvec寄存器。

在 main() 中,初始化许多设备和子系统后,将会调用 userinit(kernel/proc.c)来创建第一个进程。

在 userinit() 中,创建的进程代码为 initcode 里面的内容(user/initcode.S),在 initcode 中会请求 exec() 系统调用创建 init(user/init.c)进程,在 init.c 中会先创建 fd 0、1、2,然后 fork() 一个子进程来执行 shell,至此,整个系统启动完成。

三种IO

BIO(阻塞IO):线程发起IO请求,不管内核是否准备好IO操作,从发起请求起,线程一直阻塞,直到操作完成。
NIO(非阻塞IO):线程发起IO请求,立即返回;内核在做好IO操作的准备之后,通过调用注册的回调函数通知线程做IO操作,线程开始阻塞,直到操作完成。
AIO(异步非阻塞IO):线程发起IO请求,立即返回;内存做好IO操作的准备之后,做IO操作,直到操作完成或者失败,通过调用注册的回调函数通知线程做IO操作完成或者失败。


同步与异步

这两个概念与消息的通知机制有关。 

同步:所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。比如,调用readfrom系统调用时,必须等待IO操作完成才返回。

异步:当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。比如:调用aio_read系统调用时,不必等IO操作完成就直接返回,调用结果通过信号来通知调用者。

阻塞和非阻塞

阻塞与非阻塞与等待消息通知时的状态有关。

阻塞:阻塞是指调用结果返回前,进程会被挂起,直到调用结束得到结果再唤醒进程。

非阻塞:非阻塞指在不能立刻获得返回结果之前,不会阻塞进程,进程可以立即返回,并且设置相应的 erron。

三、Lab:System calls

切换到 syscall 分支: 

git fetch
git checkout syscall
make clean

3.1 Using gdb

该部分主要教你怎么使用 gdb 来调试 xv6。

第一步

准备两个 shell 窗口。

第二步

在一个 shell 窗口内,运行如下指令(在 xv6 仓库里面运行):

make qemu-gdb

运行后最下方会出现 tcp::26000 的字样,记住这个端口号 26000。 

第三步

在另外一个 shell 运行如下命令(也要在 xv6 仓库里面运行):

gdb-multiarch

然后在 gdb 命令窗口输入如下命令:

target remote localhost:26000

接下来就可以开始调试了,使用 file 命令可以指定调试的文件:

file kernel/kernel

使用 b 命令设置断点:

b syscall

使用 c 让程序执行,直到断点处停下:

使用 layout src/asm 查看程序当前位置的源码或者汇编:

layout src

使用 backtrace 打印函数栈,如下,可以看到我们设置断点的 syscall() 在栈顶,usertrap() 则在其下,说明在 usertrap() 函数里面调用了 syscall():

使用 n 命令单步执行,跨过 struct proc *p = myproc() 这一行后,然后执行如下命令查看 p 指针指向的内容:

p /x *p

3.2 System call tracing (moderate)

这部分要实现 trace 命令,该命令能够追踪某条命令所执行的系统调用,并且打印出来,入参是一个 mask,指定要追踪哪些系统调用。

首先在 user/user.h 中定义系统调用,该文件中的定义是提供给用户调用的: 

// user/user.h
int trace(int);

其对应实现在 usys.S 中,在执行 make 后,usys.S 会由 usys.pl 脚本生成,这个汇编函数首先将系统调用号 SYS_trace 放入寄存器 a7 中,然后执行 ecall 指令请求系统调用:

.global trace
trace:li a7, SYS_traceecallret

执行 ecall 指令后,系统会进入内核态,此时即可执行真正的系统函数,先到 syscall.h 中定义 trace 的系统调用号:

然后在 syscall.c 中加入 trace:

然后到 sysproc.c 定义系统函数 sys_trace():

uint64
sys_trace()
{int mask;argint(0, &mask);myproc()->trace_mask = mask;if(((1 << SYS_trace) & mask) == (1 << SYS_trace)){printf("%d: syscall trace -> 0\n", myproc()->pid);}return 0;
}

最后修改 syscall.c 的 syscall() 函数即可:

void
syscall(void)
{int num, mask;struct proc *p = myproc();num = p->trapframe->a7;mask = p->trace_mask;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->a0p->trapframe->a0 = syscalls[num]();if(((1 << num) & mask) == (1 << num)){printf("%d: syscall %s -> %d\n", p->pid, syscall_names[num], p->trapframe->a0);}} else {printf("%d %s: unknown sys call %d\n",p->pid, p->name, num);p->trapframe->a0 = -1;}
}

3.3Sysinfo (moderate)

这部分也要实现一个系统调用,可以返回一个结构体给用户,结构体里面包含了正在使用的进程个数,以及当前的空闲内存,这部分主要注意的地方就是内核空间的内存用户是访问不了的,所以需要使用 copyout 函数将用户空间的结构体拷贝到用户空间上,然后把结构体在用户空间上的地址返回即可。

系统调用的添加和上面一样。系统函数在 sysfile.c 里面声明:

uint64
sys_sysinfo(void)
{uint64 si_p; // user pointer to struct sysinfostruct sysinfo si;struct proc *p = myproc();argaddr(0, &si_p);si.freemem = get_free_memory();si.nproc = get_nproc();if(copyout(p->pagetable, si_p, (char *)&si, sizeof(si)) < 0)return -1;return 0;
}

获取正在使用的进程个数:

/* get the number of processes whose state is not UNUSED */
uint64 get_nproc(void)
{struct proc *p;uint64 num = 0;for(p = proc; p < proc + NPROC; p++){if(p->state != UNUSED){num++;}}return num;
}

获取空闲内存:

/* collect the amount of free memory */
uint64
get_free_memory(void)
{struct run *r;uint64 num = 0;acquire(&kmem.lock);r = kmem.freelist;while(r){num++;r = r->next;}release(&kmem.lock);return num * PGSIZE;
}

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

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

相关文章

temu上新待确认在哪里点

在Temu平台上&#xff0c;作为卖家&#xff0c;您可能需要上架新的商品以吸引更多的买家。下面是一般的步骤&#xff0c;但请注意&#xff0c;不同平台的操作可能略有不同&#xff0c;具体请参考Temu官方的帮助文档或联系客服。 先给大家推荐一款拼多多/temu运营工具——多多情…

pathlib --- 面向对象的文件系统路径

目录 基础使用 纯路径 通用性质 运算符 访问个别部分 方法和特征属性 具体路径 方法 对应的 os 模块的工具 3.4 新版功能. 源代码 Lib/pathlib.py 该模块提供表示文件系统路径的类&#xff0c;其语义适用于不同的操作系统。路径类被分为提供纯计算操作而没有 I/O 的 …

k8s部署jenkins

1.先决条件 1.因为国内的容器镜像加速器无法实时更新docker hub上的镜像资源.所以可以自己进行jenkins的容器镜像创建,. 2.这里用到了storageClass k8s的动态制备.详情参考: k8s-StoargClass的使用-基于nfs-CSDN博客 3.安装docker服务.(用于构建docker image) 2.构建jenki…

Mac右键添加通过VSCode打开

Mac右键添加通过VSCode打开 1 首先打开自动操作 进入方式 访达 – 应用程序 – 自动操作 2. 选择快速操作 3. 添加 最后 commands保存&#xff0c;可以输入自定义的名称 for f in "$" doopen -a "Visual Studio Code" "$f" done4. 找到保存的快…

KaiwuDB 多模数据库-时序性能优化

随着物联网领域的快速发展&#xff0c;时序数据的产生和处理需求不断增长。为了满足实时性、高效性和准确性的要求&#xff0c;数据库需要进行时序性能优化&#xff0c;以提供快速的数据写入、实时查询和高效的数据存储与处理能力。 本期直播介绍了时序数据和时序数据库特征以…

树莓派3B+ PCB叠层设计

板子废了&#xff0c;用电磨切了下&#xff0c;看看是什么叠层。 由于有BCM43455 WIFI芯片&#xff0c;这个是0.3ball 0.4pitch&#xff0c;肯定是要用盲孔布线的。 然后根据这个切面看&#xff0c;板子是6层的&#xff0c;外层内层铜厚应该是一样的 1-2层介质特别薄竟然<1o…

c语言练习13周(1~5)

输入任意整数n求以下公式和的平方根。 读取一系列的整数 X&#xff0c;对于每个 X&#xff0c;输出一个 1,2,…,X 的序列。 编写double fun(int a[M][M])函数&#xff0c;返回二维数组周边元素的平均值&#xff0c;M为定义好的符号常量。 编写double fun(int a[M])函…

小米智能摄像头mp4多碎片手工恢复案例

小米智能摄像头mp4多碎片手工恢复案例 智能摄像头目前在市场上极为常见&#xff0c;仅需要一张存储卡即可实现视频、音频的采集&#xff0c;同时可以通过手机APP进行远程控制&#xff0c;相比传统安防品牌成本更低、更容易部署。在智能摄像头品牌中小米算是绝对的大厂&#xf…

折扣因子的变化图(Python)

var 3 var_list [3] for _ in range(50):var * .95var_list.append(var)import matplotlib.pyplot as plt import numpy as np plt.plot(np.arange(len(var_list)), var_list, linewidth1) plt.show()

Mysql的分库分表

一、单Mysql节点 假如一主一从 为什么不能无限读&#xff1f; 瓶颈分析&#xff1a; 资源限制&#xff1a; 如CPU、内存、磁盘I/O、网络带宽等。随着读请求的增加&#xff0c;服务器的负载将会增加&#xff0c;甚至可能导致系统崩溃。 连接数限制&#xff1a; MySQL有最大连…

uniapp 使用web-view外接三方

来源 前阵子有个需求是需要在原有的项目上加入一个电子签名的功能&#xff0c;为了兼容性和复用性后面解决方法是将这个电子签名写在一个新的项目中&#xff0c;然后原有的项目使用web-view接入这个电子签名项目&#xff1b; 最近又有一个需求&#xff0c;是需要接入第三方的…

【前端】浅谈async/await异步传染性

文章目录 概述观点无法解决可以解决 来源 概述 "异步传染性"问题通常是指&#xff0c;当一个函数使用了async和await&#xff0c;其调用者也需要使用async和await处理异步操作&#xff0c;导致整个调用链都变成异步的。这种情况可能导致代码变得更复杂&#xff0c;不…

WSL2 docker GUI 界面

在 WSL2 docker 中运行GUI界面。 具体流程和远程显示Ubuntu界面类似&#xff0c;链接, 更简单一点&#xff0c; 少了 ssh 的部分。 安装好wsl2 和 docker wsl2 运行GUI程序&#xff0c;windows 会默认弹出窗口。 可以安装 gedit 测试一下 windows 下载并运行 Xlaunch. 运行 d…

轻松配置PPPoE连接:路由器设置和步骤详解

在家庭网络环境中&#xff0c;我们经常使用PPPoE&#xff08;点对点协议过夜&#xff09;连接来接入宽带互联网。然而&#xff0c;对于一些没有网络专业知识的人来说&#xff0c;配置PPPoE连接可能会有些困难。在本文中&#xff0c;我将详细介绍如何轻松配置PPPoE连接&#xff…

FO-like Transformation in QROM Oracle Cloning

参考文献&#xff1a; [RS91] Rackoff C, Simon D R. Non-interactive zero-knowledge proof of knowledge and chosen ciphertext attack[C]//Annual international cryptology conference. Berlin, Heidelberg: Springer Berlin Heidelberg, 1991: 433-444.[BR93] Bellare M…

Python知识碎片补充【侯小啾python领航班系列(十四)】

Python知识碎片补充【侯小啾python领航班系列(十四)】 大家好,我是博主侯小啾, 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ�…

与原有视频会议系统对接

要实现与原有视频会议系统对接&#xff0c;需要确保通信协议的一致性。连通宝视频会议系统可与第三方视频会议系统对接。实现与第三方会议系统对接还可以使用会议室连接器&#xff0c;可以确保不同系统之间的数据传输和交互。 具体对接流程可能因不同品牌和类型的视频会议系统而…

Matlab 点云线性指数计算(加权)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 思路其实很简单,即对每个邻近点集中的点,根据其到点集中心的距离进行加权处理(权重函数),之后再基于加权之后的点获取其协方差矩阵,最后再求取其相关的特征值,以此来获取该点的线性指数。相关公式如下所示:…

Python+Requests模块添加cookie

请求中添加cookies 对于某些网站&#xff0c;登录然后从浏览器中获取cookies&#xff0c;以后就可以直接拿着cookie登录了&#xff0c;无需输入用户 名密码。 一、在参数中添加cookie 在发送请求时使用cookies 代码示例&#xff1a; import requests # 1&#xff0c;在参数…

uniapp中解决swiper高度自适应内容高度

起因&#xff1a;uniapp中swiper组件swiper 标签存在默认高度是 height: 150px &#xff1b;高度无法实现由内容撑开&#xff0c;在默认情况下&#xff0c;swiper盒子高度显示总是 150px 解决办法思路&#xff1a; 动态设置swiper盒子的高度&#xff0c;故需要获取swiper-item盒…