为什么传统模式下的系统调用/系统被认为是"sufficiently poorly designed"?



请参阅中的注释https://github.com/torvalds/linux/blob/master/arch/x86/entry/entry_64_compat.S

我知道,因为32位syscall/sysret不会保存/恢复ESP,所以有必要在任务门中处理NMI,以确保有一个好的堆栈指针。除此之外,操作系统采用它的其他障碍是什么?是否有支持它的操作系统,或者所有操作系统都使用sysenter/sysexit在32位传统模式下进行快速系统调用?

注意:我从未处理过遗留syscall是AMD专用指令的问题。

遗留syscall的主要问题是它需要某种形式的每cpu空间来保存当前寄存器
如您所知,操作系统无法保存堆栈上的寄存器(因为ESP不会被指令更改(,也无法在保存当前堆栈之前设置其他堆栈。

在单CPU系统(意味着单处理器系统,即没有带或不带超线程的SMP(中,操作系统可以将当前寄存器保存在内存中已知的固定位置
mov DWORD [0badf00dh], esp这样的指令将地址编码为立即数,因此不需要预先设置体系结构寄存器
但是,这在SMP系统上是不起作用的,在SMP系统中,所有CPU之间共享相同的代码,除非操作系统对所有CPU使用相同的内存区域(序列化对它的访问(
请注意,您不能加载每个cpu的指针,因为这必然会覆盖一些寄存器。

另一个重要的点是,遗留的syscall并没有保存eflags,这使得编写其处理程序就像在蛋壳上行走一样
此外,该指令还任意将VMIF设置为零,这使得编写可重入代码变得更加困难。

一种解决方法是使用调用约定:操作系统可以在整个调用中将一个寄存器(或几个(标记为易失性寄存器(就像ecx已经存在的那样(
问题是,您最终可能会保存比您想象的更多的寄存器,从而使性能下降
另一个令人难以置信的解决方案可能是在运行时为每个CPU组装syscall的入口点(基本上只是修补moffset的字段(,但这非常棘手。

在64位模式下,操作系统可以依靠swapgs来拥有一个每cpu指针(或者更正确地说,每cpu基址(,用于存储当前寄存器
由于swapgs从MSR加载,因此可以在操作系统初始化期间提前设置。

请注意,在64位系统上,操作系统也可以像Linux一样使用上层GPR将esp保存到例如r8d
这适用于处理32位兼容模式程序。

长话短说:遗留的syscall使得操作系统很难将当前上下文保存在每cpu的内存区域中。

最新更新