为什么系统调用使用不同的堆栈?

Why do system calls use a different stack?

这是一个关于 的问题。

但是,让我感到惊讶的是,TSS 为不同的权限维护不同的堆栈。也就是说,用户模式和系统模式下的代码 运行 使用不同的堆栈上下文。

既然系统调用实际上是一个函数调用,为什么我们不能重用用户栈并为此创建一个新的栈帧呢?

系统调用和函数调用是完全不同的两个东西。内核 space 和用户 space 是两个独立的世界,为了简单和安全,您希望尽可能将它们分开。内核操作必须对用户程序透明,内核数据必须仅对内核可见。系统调用不仅仅是函数调用,内核完成的工作需要对用户程序保持不可见。

使用分离栈最简单的原因是它们实际上属于两个不同的程序:一个是用户space程序,另一个是操作系统。这些堆栈具有不同的大小和偏移量,因此更易于单独管理。想象一下,如果用户程序在一个已耗尽所有可用堆栈的叶函数中进行系统调用会发生什么情况:内核要么无法执行它并崩溃,要么必须检测到这一点并通过分配更多 space 在当前堆栈的顶部。这比将两个堆栈分开要复杂得多,并且更难保持它对用户程序透明(也就是说,除非您随后恢复这些更改,这会使情况更加复杂)。

除上述之外,将系统调用视为简单的函数调用,从而使内核使用与调用程序相同的堆栈,会在程序的堆栈上留下大量内核数据(基本上每个内核函数在处理系统调用时使用的局部变量)。这是不应该发生的副作用,因为它会将大量内核数据和地址暴露给 userspace,这也是一个安全问题。如果内核要使用与发出系统调用的用户程序相同的堆栈,则每个内核函数都需要“清除”在 returning 之前使用的所有局部变量(甚至清除保存的 return 堆栈上的地址),这会使整个操作系统变慢很多,并且仍然不会对用户程序透明。