为什么 Golang 在切换上下文时只保存 %rsp、%rip 和 %rbp(没有其他被调用方保存的寄存器)?



在一般的用户线程上下文切换实现(如setjmp/longjmpfunction return方式(中,我们保存和恢复被调用方保存的寄存器,但golang仅在gobuf中保存和恢复%rsp%rip%rbp

以x86_64为例,golang 使用 runtime.gosave 保存 goroutine 上下文,并使用 runtime.gogo 恢复 goroutine 上下文。

那么戈朗为什么要这样做呢?

显然,GoLang仍然使用低效的调用约定,其中唯一的调用保留(也称为非易失性(寄存器是RSP和RBP。

runtime.gosave的调用在编译器看来就像任何其他函数调用一样(即它最终在完成一些操作后返回,并且不会修改其自身堆栈帧上方的任何内容(。 与任何其他函数调用一样,调用方必须假定它销毁了所有调用破坏(易失性(寄存器(除 RSP 和 RBP 之外的所有内容(。 因此,它想要在调用中存活的任何值都必须溢出到堆栈插槽(或它们所属的其他内存位置(。

出于同样的原因,Csetjmp只需要保存调用保留的寄存器。 内核上下文切换函数是相同的。


这篇 2017 年的谷歌群组帖子说这就是它的调用约定/ABI 的工作方式,从链接的代码来看,它仍然没有得到改进。

Go 的调用约定也低效地传递堆栈上的所有参数,不像 x86-64 System V ABI 在寄存器中传递前 6 个整数参数(和前 8 个 FP(。

相关内容

  • 没有找到相关文章

最新更新