操作系统—系统调用(实验)

文章目录

  • 系统调用
    • 1.实验目标
    • 2.实验过程记录
      • (1).理解系统调用接口
      • (2).阅读argraw、argint、argaddr和argstr
      • (3).理解系统调用的解耦合实现方式
      • (4).wait系统调用的非阻塞选项实现
      • (5).yield系统调用的实现
    • 3.存在的问题及解决方案
    • 实验小结

系统调用

1.实验目标

  阅读并了解xv6内核中关于系统调用的部分,基于实验手册的要求完成几个任务:完成wait系统调用的非阻塞选项、实现yield系统调用、阅读并理解系统调用的代码。

2.实验过程记录

(1).理解系统调用接口

操作内容:在VS Code中查看并理解user/usys.pl的代码

#!/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";
}

  usys.pl是基于perl语言的代码文件,它的主要作用是为了所有当前内核的系统调用生成对应的汇编代码,简单来理解就是,它会根据传入系统调用的名字生成如下的一段系统调用对应的汇编代码:

.global $name
${name}:li a7, SYS_${name}ecallret

  这段代码就很熟悉了,首先粘贴系统调用的name作为当前汇编代码生成文件的入口(通过.global指令指定入口),之后使用li(load immediate)将系统调用的调用号座位一个立即数加载到a7上,之后调用ecall进入操作系统内核,在执行结束之后再用ret返回。

(2).阅读argraw、argint、argaddr和argstr

操作内容:在VS Code中查看并理解argraw等函数的代码

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;
}

  实际上它的过程非常简单,调用系统调用时传入的参数会被存储在当前进程的trapframe当中,它最多可以接受6个参数(这一点和Linux内核是一致的,先前我在完成第一次选做作业的自定义系统调用时,在Linux内核当中看到的基于va_args实现的系统调用接口也最多只能接受6个参数)。

  在有了argraw这个函数之后,后续就可以通过argint、argaddr和argstr三个函数来获取系统调用的参数,分别是整数、地址和字符串,这撒个函数的实现相当简单,因为地址和整数本质上都是获取整数,因此可以直接通过argraw获取对应的内容,但是argstr的实现相对复杂一点,因为C语言的字符串实际上字符数组,作为参数传入的时候会退化成指针,所以实现获取字符串的过程可以首先通过argaddr获取地址,之后再通过实现的一个fetchstr函数获取字符串的内容并且存储到buf当中:

// Fetch the nth 32-bit system call argument.
int argint(int n, int *ip) {*ip = argraw(n);return 0;
}// Retrieve an argument as a pointer.
// Doesn't check for legality, since
// copyin/copyout will do that.
int argaddr(int n, uint64 *ip) {*ip = argraw(n);return 0;
}// Fetch the nth word-sized system call argument as a null-terminated string.
// Copies into buf, at most max.
// Returns string length if OK (including nul), -1 if error.
int argstr(int n, char *buf, int max) {uint64 addr;if (argaddr(n, &addr) < 0) return -1;return fetchstr(addr, buf, max);
}

  于是我在syscall.c当中又找到了fetchstr函数的实现细节:

// Fetch the nul-terminated string at addr from the current process.
// Returns length of string, not including nul, or -1 for error.
int fetchstr(uint64 addr, char *buf, int max) {struct proc *p = myproc();int err = copyinstr(p->pagetable, buf, addr, max);if (err < 0) return err;return strlen(buf);
}

  fetchstr的实现也并不复杂,不过因为处于内核态,它的实现不像我们会使用的类似strcpy之类的函数,首先获取到当前的进程控制块到p,然后从p的页表中拷贝出对应字节、对应地址的所有数据,在没有发生报错的时候则会正常返回字符串的长度。

(3).理解系统调用的解耦合实现方式

操作内容:在VS Code中查看并理解syscall.c的代码

static uint64 (*syscalls[])(void) = {
[SYS_fork] sys_fork,   [SYS_exit] sys_exit,     
[SYS_wait] sys_wait,     [SYS_pipe] sys_pipe,
[SYS_read] sys_read,   [SYS_kill] sys_kill,     
[SYS_exec] sys_exec,     [SYS_fstat] sys_fstat,
[SYS_chdir] sys_chdir, [SYS_dup] sys_dup,       
[SYS_getpid] sys_getpid, [SYS_sbrk] sys_sbrk,
[SYS_sleep] sys_sleep, [SYS_uptime] sys_uptime, 
[SYS_open] sys_open,     [SYS_write] sys_write,
[SYS_mknod] sys_mknod, [SYS_unlink] sys_unlink, 
[SYS_link] sys_link,     [SYS_mkdir] sys_mkdir,[SYS_close] sys_close, [SYS_rename] sys_rename,
};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;}
}

  这个过程也比较简单的,syscall.c当中将所有的系统调用存储在同一个指针数组当中,在具体调用某个系统调用的时候,syscall函数会通过trapframe当中的a7来访问刚刚存入的系统调用号,之后再直接通过下标访问就可以比较轻松地访问到对应的系统调用了,这里也可以看到,在根据系统调用号找到了对应的系统调用之后会执行这条指令:

    p->trapframe->a0 = syscalls[num]();

  也就是说,系统调用的执行结果会被存储在a0当中。

(4).wait系统调用的非阻塞选项实现

操作内容:参考实验手册要求,修改wait系统调用的实现系统,增加一个非阻塞选项参数flags
  为了增加sys_wait的非阻塞版本,我们首先需要在sys_wait中增加获取整型变量flags作为非阻塞标志的流程:

uint64 sys_wait(void) {uint64 p;if (argaddr(0, &p) < 0) return -1;int flags;if (argint(1, &flags) < 0) return -1;return wait(p, flags);
}

  这个过程是比较简单的,参考argraw、argint的实现可以知道,argint函数第一个参数传入的实际上是系统调用参数的位置,因为flags显然是在第二个位置上的参数,因此仿照上面获取地址p的方式,获取flags参数,之后与p一起传入wait函数即可,那么接下来就可以修改wait系统调用的函数声明了

int             wait(uint64, int);

  接下来就需要直接修改wait系统调用的实现本身了:

int wait(uint64 addr, int flags) {if (flags != 1) sleep(p, &p->lock);  // DOC: wait-sleepelse {release(&p->lock);return -1;}}
}

  这里省略了wait函数的一部分,前面一部分的代码没有改动,具体的wait系统调用的实现已经再上一次的实验中分析过了,因此这里只需要改动最后一部分是否阻塞即可。
  当flags不为1的时候就阻塞等待,否则释放当前获取的自旋锁,然后返回-1,这样就可以完成非阻塞等待的全流程了,接下来尝试使用waittest完成对于wait系统调用的测试:
在这里插入图片描述
  可以看到,waittest在xv6内已经测试完毕,之后在内核仓库的目录下使用grade-lab-syscall进行测试:
在这里插入图片描述

(5).yield系统调用的实现

操作内容:参考实验手册要求,增加一个新的yield系统调用
  首先在kernel/syscall.h当中增加一个SYS_yield系统调用号,这里直接继承最后一个系统调用后的第一个系统调用号23:
在这里插入图片描述
  之后再kernel/syscall.c当中增加sys_yield的声明,并且在系统调用表syscalls当中增加刚刚添加的系统调用sys_yield:
在这里插入图片描述
  之后再sysproc.c当中添加sys_yield函数的定义:

uint64 sys_yield(void) {struct proc *p = myproc();uint64 pc = p->trapframe->epc;printf("start to yield, user pc %p\n", pc);yield();return 0;
}

  因为yield函数本身实现非常简单,所以不需要单独传参,为了满足实验手册的要求,在sys_yield当中获取了当前进程的PCB,从它的trapframe中获取epc即为进入trap时的用户pc,这里没有对PCB进行加锁,这是因为struct proc将trapframe划归为私有部分,访问时不需要持有锁。
然后需要再user.h和usys.pl当中添加代码
在这里插入图片描述
在这里插入图片描述
  最后,在Makefile的UPROGS增加_yieldtest的编译目标:
在这里插入图片描述
  之后编译运行,在xv6中执行yieldtest测试,发现效果是一致的:
在这里插入图片描述
  然后我尝试使用了grade-lab-syscall进行测试,发现有一个叫做time的点一直没有办法通过:
在这里插入图片描述
  于是我去MIT的课程网站上查看了一下实验要求,发现需要自行创建一个time.txt,然后写入做实验的时间:
在这里插入图片描述
  在创建了time.txt并且写入实验时间之后就100分通过了测试:
在这里插入图片描述

3.存在的问题及解决方案

问题:为什么在多CPU的情况下测试yield会出现乱码?

  首先我们来看看测试的时候会发生什么:
在这里插入图片描述
  可以发现,其实这一段输出信息混乱并不是完全没有规律,实际上应该是多个不同的需要打印出来的内容因为一些问题混杂在了一起,我猜测这个问题可能出在打印过程没有对缓冲区加锁,但是参考了printf.c当中对printf的实现,它实际上是加锁了的:

// Print to the console. only understands %d, %x, %p, %s.
void
#ifdef TEST
_printf(const char *filename, unsigned int line, char *fmt, ...)
#else
_printf(char *fmt, ...)
#endif
{…locking = pr.locking;if (locking) acquire(&pr.lock);#endifif (locking) release(&pr.lock);
}void printfinit(void) {initlock(&pr.lock, "pr");pr.locking = 1;
}

  所以问题应该不在给输出过程加锁这件事上,最终我猜测是多核调度的过程中,不同核心上运行的进程的缓冲区不同步,而缓冲区到IO设备上这个过程可能是无法保证线程安全的,因此导致了输出也不同步的问题。

实验小结

  • 1、本次实验阅读了xv6内核中系统调用的实现细节,了解了系统调用的实现与调用是如何分离的。
  • 2、完成了对于wait系统调用的修改,完成了非阻塞式的wait系统调用,之后还增加了yield系统调用,最后通过grade-lab-syscall完成了这次修改/增加的两个系统调用的测试。

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

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

相关文章

酷开科技逐步为用户构建健全的智慧家庭生活场景

大规模与精细化人群技术则是通过大量的计算能力和精细化的运营能力&#xff0c;建立用户专属数据储存区域&#xff0c;使得用户在使用不同电视的观影偏好和兴趣能够能够得以延续。 不拘泥于自有品牌终端数量&#xff0c;酷开系统除了集成在创维电视上&#xff0c;还服务于飞利…

http是什么?http的基础知识教程详解(2024-04-24)

1、http的概念 HTTP&#xff08;超文本传输协议&#xff0c;HyperText Transfer Protocol&#xff09;是一种用于分布式、协作式、超媒体信息系统的应用层协议。 HTTP 是万维网&#xff08;WWW&#xff09;的数据通信的基础&#xff0c;设计目的是确保客户端与服务器之间的通…

CUDA的开发框架

CUDA的开发框架主要提供了一系列工具和库&#xff0c;使得开发者可以充分利用NVIDIA GPU进行高效的并行计算。以下是CUDA开发框架的一些关键组成部分。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1.CUDA核心库&#xff1a;这些是构…

苍穹外卖day11 Apache ECharts 数据统计-图形报表

文章目录 前言一、Apache ECharts二、营业额统计1. 业务规则2. 接口设计3. 代码实现 三、用户统计1. 业务规则2. 接口设计3. 代码实现 四、订单统计1. 业务规则2. 接口设计3. 代码实现 五、销量排名Top101. 业务规则2. 接口设计3. 代码实现 前言 作为后端开发人员使用Echarts&…

2024年阿里云新用户购买云服务器,六大优惠活动介绍

假如你是一个初次购买阿里云服务器产品的新手用户&#xff0c;在购买阿里云服务器过程中如果直接注册账号然后通过云服务器ECS产品页下单购买可能会多花很多钱&#xff0c;作为一个老用户&#xff0c;有必要把这2024年新手用户必须了解的六大优惠活动介绍给大家&#xff0c;让大…

护眼灯到底有用吗?必选机型护眼灯十大品牌推荐

护眼灯到底有用吗&#xff1f;答案是有用的。护眼灯不是智商税&#xff0c;尤其对有娃、爱玩游戏的人士。儿童青少年近视率高&#xff0c;学习环境照明影响大。普通灯光不足以满足需求&#xff0c;蓝光频闪加剧用眼疲劳&#xff0c;导致近视。选择适合孩子的护眼灯至关重要&…

Linux--内核移植(二)移植流程及驱动修改

本文来总结一下如何将 NXP 官方提供的 Linux 内核移植到正点原子的 I.MX6U-ALPHA 开发板上。 一、官方开发板内核测试 NXP 提供的 Linux 源码肯定是可以在自己的 I.MX6ULL EVK 开发板上运行下去的&#xff0c;所以我们肯定是以 I.MX6ULL EVK 开发板为参考&#xff0…

AWVS的使用

AWVS的使用 1、使用docker拉取AWVS的镜像 docker pull secfa/docker-awvs 2.使用AWVS docker run -it -d -p 13443:3443 --cap-add LINUX_IMMUTABLE secfa/docker-awvs 3.访问 4.输入账号密码 https://hub.docker.com/r/secfa/docker-awvs 找到账号密码 username:adminadmin.…

OpenCV如何实现拉普拉斯算子的离散模拟

返回:OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;OpenCV的Sobel 衍生品 下一篇 &#xff1a;OpenCV 如何实现边缘检测器 目标 在本教程中&#xff0c;您将学习如何&#xff1a; 使用 OpenCV 函数 Laplacian&#xff08;&#xff09; 实…

Django模型继承之多表继承

在Django模型继承中&#xff0c;支持的第二种模型继承方式是层次结构中的每个模型都是一个单独的模型。每个模型都指向分离的数据表&#xff0c;并且可以被独立查询和创建。在继承关系中&#xff0c;子类和父类之间通过一个自动创建的OneToOneField进行连接。示例代码如下&…

NCF29A1 高端阻抗匹配

一、前言 Class E 高端 L-Front 匹配集成了额外的滤波器&#xff0c;提供了足够的谐波衰减&#xff0c;使 NCF29A1 与天线在比基频更高的频率下具有相当大的增益。向 PA 提供的阻抗和输出电容与表 1 ZPAOUT 所示相同。 二、原理图 图 1 高端 L-Front 匹配原理图 1&#xff…

VNISEdit 制作安装包

1. 环境依赖 1.1. NSIS 下载 下载地址&#xff1a;https://nsis.sourceforge.io/Download 1.2. VNISEdit 下载 下载地址1&#xff1a;https://sourceforge.net/projects/hmne/ 下载 exe 安装。 下载地址2&#xff1a;https://hmne.sourceforge.net/ 可以下载 exe 安装。也…

实体书营销:“三三裂变”,实操细节分享……

实体书营销:“三三裂变”,实操细节分享 一、实验结果 “三三裂变”的实验,结果比较好。就是我们大概有300人报名,但实际行动的只有109人,大概有103人都完成了三个人的目标,也就是说我们通过109人裂变了475人,利润率是1:4.5左右,整个裂变的效率还是可以的,也就是说: …

K-means聚类算法:如何在杂乱无章的数据中找出规律?

什么是K-means聚类算法&#xff1f; 在编程的世界里&#xff0c;K-means聚类算法就像一位无私的指路人&#xff0c;它不需要我们给出明确的指示&#xff0c;只需要我们提供数据&#xff0c;它就能帮助我们找到数据的归属&#xff0c;找到数据的“家”。 K-means聚类算法的名字…

1000. 合并石头的最低成本(H)/ 1312. 让字符串成为回文串的最少插入次数(H)/ 区间DP!!!

1000. 合并石头的最低成本 官方题解&#xff08;含python&#xff09;其他题解 1312. 让字符串成为回文串的最少插入次数 官方题解 class Solution:def minInsertions(self, s: str) -> int:n len(s)# dp[i][j] 表示对于字符串 s 的子串 s[i:j]&#xff08;这里的下标从…

【Python】基础知识(函数与数据容器)

笔者在C语言基础上学习python自用笔记 type() 返回数据类型 name "root" hei 1.8 wei 77 type_hei type(hei) type_wei type(wei) print(type(name)) print(type_hei) print(type_wei)在python中变量是没有类型的&#xff0c;它存储的数据是有类型的。 数据类…

如何删除“上传到移动云盘”右键菜单

问题表现 如题&#xff0c;“上传到移动云盘”的右键菜单非常难删除&#xff0c;按照网上的右键菜单注册表删除通用教程也没找到 解决方法 实际上&#xff0c;该菜单的位置在注册表中的 计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AllFilesystemObjects\shell\就是删除里…

Kafka 可视化管理工具 CMAK 启动错误 -- 命令行太长 问题解决

一、安装环境描述&#xff1a; Kafka版本&#xff1a;kafka_2.13-2.8.1cmak 版本&#xff1a;cmak-3.0.0.6安装环境&#xff1a;windows 11 二、问题描述 当我们在 命令行启动 cmak.bat 命令时&#xff0c;会报如下错误&#xff1a; 命令行太长三、解决办法 修改 cmak.bat…

CUDA线程管理

核函数在主机端启动时&#xff0c;执行会转移到设备上&#xff0c;并且将控制权转移回主机。当核函数在GPU上运行时&#xff0c;主机可以运行其他函数。因此&#xff0c;主机与核函数是异步的。 此时&#xff0c;设备端也就是GPU上会产生大量的线程&#xff0c;并且每个线程都…

世界读书日|看看阿里内部的技术大牛们推荐了什么书?

AI 时代&#xff0c;技术呈指数级发展&#xff0c;我们越来越关注&#xff0c;如何持续学习提升&#xff0c;跟上时代的步伐。 刚好借着世界读书日的契机&#xff0c;我们邀请了阿里内部的技术大牛们&#xff0c;分享他们在 AI 时代持续学习的过程中&#xff0c;推荐的一些内容…