x86-64 return 指令会在 linux 中导致页面错误吗?当前进程栈是否总是在主存中?

Can x86-64 return instruction cause a page fault in linux? Is the current process stack always in main memory?

我知道 return 指令会将“程序控制转移到位于堆栈顶部的 return 地址” (page 1205)

  1. 当前进程的堆栈是否总是在内存中?

  2. 假设我return到我自己程序中的一个函数(接近return?),那么我能保证没有页面错误吗?

  3. 如果 return 将控制权交还给另一个不在内存中的段中的代码(可能像上下文切换),return 会导致页面错误吗?

就 CPU 或内核页面驱逐算法而言,ret 或 call/return 通常没有什么特别之处。(CPU 确实有一个针对 call/ret 的特殊分支预测器,但这不会影响 OS 关于页面驱逐的决定。)


User-space 堆栈内存就像任何其他 user-space 内存一样按需分页(除非您使用 mlock)。 ret 从堆栈中弹出一个 return 地址作为 [rsp];这是可能使 ret 本身出错的内存访问。 (当然,如果 ret 指令本身的代码提取出现故障)。

ret 成功执行后,从新 RIP 中获取代码如果碰巧被驱逐,可能会 also/instead 页面错误。 (或者,如果 call 指令位于页面的末尾,则被 return 编辑的页面可能永远不会被触及。)

(当然,在手写 asm 或 retpolines 中,call/ret 可能不匹配。例如 push/ret 等同于 jmp。很明显,你可以跳转到以前未触及或一段时间未触及的页面,从而导致硬或软页面错误。)


就 CPU 或内核页面驱逐算法而言,ret 或 call/return 通常没有什么特别之处。包含 [rsp] 的页面往往很热并且不会被驱逐,但是 return 从一个长的 sleep(100) 系统调用中,会给内核足够的时间来驱逐一个页面。尤其是在内存压力很大的情况下。或者,如果函数使用大量堆栈 space,它们可能会使较低的页面保持热状态,最终 return 备份调用树可能会从尚未加载的页面加载 return 地址即使进程没有休眠也有一段时间没有被触及。