扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
内核中的处理和返回
sysenter_entry 整个的实现可以参见 arch/i386/kernel/entry.S。 内核处理 SYSENTER 的代码和处理 INT 的代码不太一样。通过 sysenter 指令进入 Ring 0 之后,由于当前的 ESP 并非指向正确的内核栈,而是当前 CPU 的 TSS 结构中的一个缓冲区(参见上文),所以首先要解决的是修复 ESP,幸运的是,TSS 结构中 ESP0 成员本身就保存有 Ring 0 状态的 ESP 值,所以在这里将 TSS 结构中 ESP0 的值赋予 ESP 寄存器。将 ESP 恢复成指向正确的堆栈之后,由于 SYSENTER 不是通过调用门进入 Ring 0,所以在堆栈中的上下文和使用 INT 指令的不一样,INT 指令进入 Ring 0 后栈中会保存如下的值。
低地址
返回用户态的EIP
用户态的CS
用户态的EFLAGS
用户态的ESP
用户态的SS(和DS相同)
高地址
因此,为了简化和重用代码,内核会用 pushl 指令往栈中放入上述各值,值得注意的是,内核在栈中放入的相对应用户态 EIP 的值,是一个代码标签 SYSENTER_RETURN,在 vsyscall-sysenter.S 可以看到,它就在 sysenter 指令的后面(在它们之间,有一段 NOP,是内核返回出错时的处理代码)。接下来,处理系统调用的代码就和中断方式的处理代码一模一样了,内核保存所有的寄存器,然后系统调用表找到对应系统调用的入口,完成调用。最后,内核从栈中取出前面存入的用户态的 EIP 和 ESP,存入 edx 和 ecx 寄存器,调用 SYSEXIT 指令返回用户态。返回用户态之后,从栈中取出 ESP,edx,ecx,最终返回 glibc 库。
其它操作系统以及其它硬件平台的支持
值得一提的是,从 Windows XP 开始,Windows 的系统调用方式也从软中断 int 0x2e 转换到采用 sysenter 方式,由于完全不再支持 int 方式,因此 Windows XP 的对 CPU 的最低配置要求是 PentiumII 300MHz。在其它的操作系统例如 *BSD 系列,目前并没有提供对 sysenter 指令的支持。
在 CPU 方面,AMD 的 CPU 支持一套与之对应的指令 SYSCALL/SYSRET。在纯 32 位的 AMD CPU 上,还没有支持 sysenter 指令,而在 AMD 推出的 AMD64 系列 CPU 上,处于某些模式的情况下,CPU 能够支持 sysenter/sysexit 指令。在 Linux 内核针对 AMD64 架构的代码中,采用的还是 SYSCALL/SYSRET 指令。至于这两种指令最终谁将成为标准,目前还无法得出结论。
未来
我们将 Intel 的 sysenter/sysexit 指令,AMD 的 SYSCALL/SYSRET 指令统称为"快速系统调用指令"。"快速系统调用指令"比起中断指令来说,其消耗时间必然会少一些,但是随着 CPU 设计的发展,将来应该不会再出现类似 Intel Pentium4 这样悬殊的差距。而"快速系统调用指令"比起中断方式的系统调用方式,还存在一定局限,例如无法在一个系统调用处理过程中再通过"快速系统调用指令"调用别的系统调用。因此,并不一定每个系统调用都需要通过"快速系统调用指令"来实现。比如,对于复杂的系统调用例如 fork,两种系统调用方式的时间差和系统调用本身运行消耗的时间来比,可以忽略不计,此处采取"快速系统调用指令"方式没有什么必要。而真正应该使用"快速系统调用指令"方式的,是那些本身运行时间很短,对时间精确性要求高的系统调用,例如 getuid、gettimeofday 等等。因此,采取灵活的手段,针对不同的系统调用采取不同的方式,才能得到最优化的性能和实现最完美的功能。
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者