首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

一个操作系统的设计与实现——第23章 快速系统调用

编程知识
2024年09月01日 09:28

23.1 什么是快速系统调用

系统调用是操作系统为3特权级任务提供服务的一种手段。在32位操作系统中,我们通过中断实现了系统调用。由于系统调用是一个使用非常频繁的机制,且中断也不是专门为系统调用设计的,因此,64位CPU提供了系统调用的专用机制:快速系统调用。

快速系统调用由专用的syscall指令发起,并由专用的sysret指令返回。syscall必须从3特权级转移到0特权级,sysret必须从0特权级返回到3特权级。快速系统调用全程使用寄存器传参,并且系统调用函数的cs:rip是预设好的,因此,syscall/sysret均不需要参数。

综上,快速系统调用的整套机制都是非常固定的,这就带来了高效率。

23.2 快速系统调用的安装

在使用快速系统调用之前,需要先安装好快速系统调用所需的组件,这涉及到4个MSR。

23.2.1 IA32_EFER

快速系统调用这个功能在初始状态下是关闭的,其开关位于IA32_EFER的第0位。这个MSR我们已经见过了,它的编号为0xc0000080

23.2.2 IA32_STAR

这个MSR的低32位是保留位;第32~47位用于设定syscall使用的0特权级段选择子;第48~63位用于设定sysret使用的3特权级段选择子。

注意,这里没有说设定的是"代码段选择子",而仅仅是"段选择子",这是因为选择子的设定有一套比较奇怪的定义:

  • 对于第32~47位,其数值本身会被视为0特权级代码段选择子;这个数值加8得到的数值会被视为0特权级数据段选择子
  • 对于第48~63位,其数值本身会被视为3特权级兼容模式代码段选择子;这个数值加8得到的数值会被视为3特权级数据段选择子;这个数值加16得到的数值会被视为3特权级IA32-e模式代码段选择子。那么,当执行sysret时,其到底选择哪个代码段呢?这个问题将在下文中讨论

段选择子是描述符索引值左移3位得到的,因此加8即为GDT中的下一个描述符。也就是说,第32~47位设定的是两个连续的段描述符中的第一个;第48~63位设定的是三个连续的段描述符中的第一个。不过,由于我们的操作系统从不使用兼容模式代码段,因此在GDT中并没有定义这个描述符。

这个MSR的编号为0xc0000081

23.2.3 IA32_LSTAR

这个MSR用于设定系统调用函数的地址,其编号为0xc0000082

23.2.4 IA32_FMASK

这个MSR用于设定RFLAGS屏蔽掩码。具体来说,当执行syscall时,rflags会变成这样:rflags &= ~IA32_FMASK。在我们的操作系统中,这个MSR用于屏蔽IF位,屏蔽掩码为0x200

这个MSR的编号为0xc0000084

23.3 syscall的执行细节

当执行syscall时,CPU会执行以下操作:

  • rcx = rip
  • r11 = rflags
  • cs = IA32_STAR[32:47]
  • rip = IA32_LSTAR
  • rflags &= ~IA32_FMASK

也就是说,rcxr11会被syscall使用,它们不能用于传参。此外,syscall不会对rsp做任何处理,这是一个很重要的问题,我们将在下文中讨论。

23.4 sysret的执行细节

当执行sysret时,CPU会执行以下操作:

  • rip = rcx
  • rflags = r11
  • 如果sysret没有64位前缀,则:cs = IA32_STAR[48:63];否则:cs = IA32_STAR[48:63] + 16

也就是说:

  1. 操作系统需要保护rcxr11
  2. sysret需要具有64位前缀

上述第1点将在下文中讨论;第2点在nasm中可使用o64 sysret实现。

23.5 系统调用的实现

请看本章代码23/Syscall.h

第3行,声明了syscallInit函数。这个函数是用汇编语言实现的。

接下来,请看本章代码23/Syscall.s

第15~18行,将IA32_EFER的第0位置1,打开快速系统调用功能。

第20~23行,设定IA32_STAR。在GDT中,3号描述符是0特权级代码段,4号描述符是0特权级数据段,这两个段描述符对应于IA32_STAR的第32~47位;5号描述符是3特权级数据段,6号描述符是3特权级代码段,没有兼容模式代码段,因此,这里应强行将4号描述符安装到IA32_STAR的第48~63位,使得5号和6号描述符处于正确的位置。

第25~29行,将系统调用函数syscallHandle的地址安装到IA32_LSTAR

第31~34行,将屏蔽掩码0x200安装到IA32_FMASK

至此,快速系统调用准备完毕。

syscallHandle函数为系统调用函数。在32位操作系统中,系统调用由中断实现,中断发生时,CPU会自动切换到0特权级栈,由于0特权级栈是操作系统提供的,所以能够保证它的安全。那么,什么叫"安全的栈"?如果不切换栈,到底有什么问题?请看下例:

void test()
{
    char s[] = "666";
    __asm__ __volatile__("syscall");
}

将这段代码翻译成汇编语言,可以是:

test:
	mov dword [rsp - 4], '666'
	syscall
	ret

可以发现:这个函数的rsp是没有也不需要实际减去4的,但如果将这样的rsp提供给系统调用函数使用,就是错误的,因为系统调用函数不知道栈到底应该怎么用。这就是不安全栈带来的问题,因此,在系统调用时,切换到一个安全的栈是有必要的。

然而,syscall不会自动切换栈,我们需要手动完成这个操作。0特权级栈在TSS中,TSS的地址是0xffff800000092000,但想要使用这个地址,就必须先用一个寄存器周转64位立即数。用哪个寄存器呢?无关乎ABI,似乎用哪个都不完美。此时,我们之前设定的IA32_GS_BASE派上了用场,使用gs就可以直接操作TSS了。不仅如此,我们的操作系统的TSS是延长到128字节的,104字节以后的一小段内存可用于在换栈前备份当前的rsp。至此,换栈问题就完美解决了。

第44行,将rsp备份到[TSS + 104]

第45行,切换到0特权级栈。

第47~48行,保护rcxr11。现在的栈是安全的,可以放心使用。

第50~51行,调用rax指定的函数。

第53~54行,恢复rcxr11

第56行,恢复3特权级栈。

第58行,从快速系统调用返回。

第60~63行,定义了系统调用表。1号系统调用保留给后续章节使用。

接下来,请看本章代码23/Start.s

_start函数是3特权级任务的真正入口,其用于使任务在结束后自动退出。

23.6 编译与测试

本章代码23/Makefile增加了Syscall.sStart.s的编译与链接命令。

本章代码23/Kernel.c23/Test.c测试了0与2号系统调用。

From:https://www.cnblogs.com/yingyulou/p/18391066
本文地址: http://shuzixingkong.net/article/1625
0评论
提交 加载更多评论
其他文章 MySQL的索引原理及使用
B+树的最底层叶子节点包含了所有的索引项。从图上可以看到,B+树在查找数据的时候,由于数据都存放在最底层的叶子节点上,所以每次查找都需要检索到叶子节点才能查询到数据。所以在需要查询数据的情况下每次的磁盘的IO跟树高有直接的关系,但是从另一方面来说,由于数据都被放到了叶子节点,
MySQL的索引原理及使用 MySQL的索引原理及使用 MySQL的索引原理及使用
喜报!Fluent Editor 开源富文本迎来了第一位贡献者!
你好,我是 Kagol,个人公众号:前端开源星球。 2024年8月20日,刚开源一周的富文本 Fluent Editor 迎来了第一位贡献者:zzxming,带大家一起分析下这个 #10 这个PR。
喜报!Fluent Editor 开源富文本迎来了第一位贡献者! 喜报!Fluent Editor 开源富文本迎来了第一位贡献者! 喜报!Fluent Editor 开源富文本迎来了第一位贡献者!
全网最适合入门的面向对象编程教程:42 Python常用复合数据类型-collections容器数据类型
在Python中,collections模块提供了一组高效、功能强大的容器数据类型,扩展了内置的基础数据类型(如list、tuple、dict等),这些容器数据类型在处理特定问题时,能够提供更简洁、更高效的解决方案。
全网最适合入门的面向对象编程教程:42 Python常用复合数据类型-collections容器数据类型 全网最适合入门的面向对象编程教程:42 Python常用复合数据类型-collections容器数据类型 全网最适合入门的面向对象编程教程:42 Python常用复合数据类型-collections容器数据类型
大请求、请求超时问题
耗时很长的请求怎么处理?比如数据量大的。业务逻辑处理时间太久,以至于响应超时 这里的超时响应指的是ReadTimeOut,即发送请求内容完毕到接收响应数据开始的这段时间。普通HTTP请求可能在这段时间没有响应超时。 HTTP分块传输(Chunked Transfer Encoding)中每个数据块的
大请求、请求超时问题 大请求、请求超时问题 大请求、请求超时问题
Dify大语言模型应用开发平台新手必备:安装注册与私有服务器部署全步骤
Dify简介 Dify是一个开源的大语言模型(Large Language Model, LLM)应用开发平台。它融合了后端即服务(Backend as a Service, BaaS)和LLMOps的理念,旨在帮助开发者,甚至是非技术人员,能够快速搭建和部署生成式AI应用程序。 Dify的主要特点
Dify大语言模型应用开发平台新手必备:安装注册与私有服务器部署全步骤 Dify大语言模型应用开发平台新手必备:安装注册与私有服务器部署全步骤 Dify大语言模型应用开发平台新手必备:安装注册与私有服务器部署全步骤
gcc/g++编译
编译工具链 我们写程序的时候用的都是集成开发环境 (IDE: Integrated Development Environment),集成开发环境可以极大地方便我们程序员编写程序,但是配置起来也相对麻烦。在 Linux 环境下,我们用的是编译工具链,又叫软件开发工具包(SDK:Software De
gcc/g++编译 gcc/g++编译 gcc/g++编译
一种优雅的方式整合限流、幂等、防盗刷
大家在工作中肯定遇到过接口被人狂刷的经历,就算没有经历过,在接口开发的过程中,我们也需要对那些容易被刷的接口或者和会消耗公司金钱相关的接口增加防盗刷功能。例如,发送短信接口以及发送邮件等接口,我看了国内很多产品的短信登录接口,基本上都是做了防盗刷,如果不做的话,一夜之间,也许公司都赔完了┭┮﹏┭┮。
一种优雅的方式整合限流、幂等、防盗刷 一种优雅的方式整合限流、幂等、防盗刷 一种优雅的方式整合限流、幂等、防盗刷
如何实现一个通用的接口限流、防重、防抖机制
介绍 最近上了一个新项目,考虑到一个问题,在高并发场景下,我们无法控制前端的请求频率和次数,这就可能导致服务器压力过大,响应速度变慢,甚至引发系统崩溃等严重问题。为了解决这些问题,我们需要在后端实现一些机制,如接口限流、防重复提交和接口防抖,而这些是保证接口安全、稳定提供服务,以及防止错误数据 和