首页 > 解决方案 > 每个系统调用的代码是否加载到每个进程的地址中?

问题描述

我已经阅读了一些文章并在 youtube 上观看了一些有关系统调用的视频。我注意到的是,系统调用通常不需要上下文切换到另一个进程。相反,它是在调用它的任何进程的上下文中处理的。我读到系统调用的内核代码和系统调用的表被放置在每个进程的地址空间内。在进程的地址空间中,内核代码位于顶部,调用堆栈位于其下方。但是该代码只能在系统调用中访问。当一个特定的系统调用开始执行时,它的栈帧也被放入进程的调用栈中。

所以我的问题是,所有这些何时以及如何发生 - 系统调用的内核代码和系统调用的表的位置?

标签: operating-system

解决方案


最常见的情况是 CPU 支持某种特权级别。操作系统将其代码和数据映射到每个虚拟地址空间作为“需要最高权限级别才能访问”(在称为“内核空间”的区域中),并且正常进程使用“最低权限级别”,因此如果他们尝试访问CPU 拒绝允许内核中的任何内容。

然而; CPU 还提供各种特殊的控制传输,操作系统可以为 IRQ 和系统调用等配置。

通常,对于系统调用,程序可能会调用类似“ write()”的东西,它将调用(动态链接/共享)库中的普通函数(导致“write()”被添加到调用堆栈中,因为它只是一个普通函数,并且不是系统调用本身)。在库内部,将有代码来打乱值并为完全不同的调用约定做准备,该约定使用某种特殊指令(int 0x80syscallsysenter或...),旨在更改 CPU 的特权级别并将控制权转移到内核的系统调用处理程序。这种特殊的控制转移不会出现在调用堆栈上(主要是因为 CPU 切换到完全不同的堆栈作为该特殊指令的一部分)。

一旦到达内核的系统调用处理程序;它可能(如果内核不是用汇编编写的)在寄存器中移动更多的东西以匹配编译器使用的正常调用约定,并使用“函数号”参数来查找/调用预期的函数(通常使用函数指针数组,使用“是正常范围内的函数编号”检查以避免“索引超出范围”问题)。

注意:这只是最常见的情况(对于 Windows、OSX、Linux 等来说非常正常);但是不同的 CPU 和不同的操作系统是不同的,有时相同的硬件/相同的操作系统的行为是不同的。例如,如果操作系统在启用“Meltdown 漏洞解决方法”的情况下运行,它可能会在其系统调用处理程序的早期进行虚拟地址空间切换。

所以我的问题是,所有这些何时以及如何发生 - 系统调用的内核代码和系统调用的表的位置?

其中大部分是在内核初始化期间设置的(在引导期间,可能在第一个正常进程启动之前);并且一旦设置它只是永久存在(例如,每当创建新的虚拟地址空间时,“内核空间”被保留/克隆/映射到每个新的虚拟地址空间)。


推荐阅读