Linux kernel 在linux 2.6中的开关_to()中保存通用寄存器

Linux kernel 在linux 2.6中的开关_to()中保存通用寄存器,linux-kernel,x86,low-level,context-switch,Linux Kernel,X86,Low Level,Context Switch,我在链接中的“Linux中x86上下文切换的演变”一文中看到了切换到的代码 大多数版本的切换到仅保存/恢复ESP/RSP和/或EBP/RBP,而不是内联asm中的其他保留呼叫的寄存器。但是Linux2.2.0版本确实将它们保存在这个函数中,因为它使用软件上下文切换,而不是依赖硬件TSS。后来的Linux版本仍然进行软件上下文切换,但没有这些push/pop指令 寄存器是否保存在其他函数中(可能在schedule()函数中)?或者不需要在内核上下文中保存这些寄存器 (我知道当系统进入内核模式时,这

我在链接中的“Linux中x86上下文切换的演变”一文中看到了
切换到
的代码

大多数版本的
切换到
仅保存/恢复ESP/RSP和/或EBP/RBP,而不是内联asm中的其他保留呼叫的寄存器。但是Linux2.2.0版本确实将它们保存在这个函数中,因为它使用软件上下文切换,而不是依赖硬件TSS。后来的Linux版本仍然进行软件上下文切换,但没有这些push/pop指令

寄存器是否保存在其他函数中(可能在
schedule()
函数中)?或者不需要在内核上下文中保存这些寄存器


(我知道当系统进入内核模式时,这些用户上下文的寄存器保存在内核堆栈中)。

2.2.0之前的Linux版本使用硬件任务切换,TSS为您保存/恢复寄存器。这就是
“ljmp%0\n\t”
正在做的事情。(
ljmp
是远jmp的AT&T语法,可能是一个任务门)。我不太熟悉硬件TSS的东西,因为它不是很相关;在现代内核中,它仍然用于获取指向中断处理程序内核堆栈的RSP,但不用于任务之间的上下文切换

硬件任务切换很慢,所以后来的内核会避免它。Linux 2.2在交换堆栈之前/之后使用
push
/
pop
手动保存/恢复保留调用的寄存器。EAX、EDX和ECX被声明为伪输出(
“=a”(EAX)、“=d”(EDX)、“=c”(ECX)
),因此编译器知道这些寄存器的旧值不再可用

这是一个明智的选择,因为
switch_to
可能在非内联函数中使用。调用方将进行一个函数调用,该调用最终返回(在运行另一个任务一段时间后),并恢复保留调用的寄存器,并且调用clobbered寄存器clobbered,就像常规函数调用一样。(因此,使用
开关_to
宏的函数的编译器代码生成器不需要在内联asm之外发出保存/还原代码)。如果您考虑在asm(而不是内联asm)中编写一个完整的上下文切换函数,您将免费获得这种易失性寄存器的碰撞,因为调用方希望这样

那么以后的内核如何避免在内联asm中保存/恢复这些寄存器呢?

Linux 2.4使用
“=b”(last)
作为输出操作数,因此编译器必须在使用此asm的函数中保存/还原EBX。asm仍然保存/恢复ESI、EDI和EBP(以及ESP)。文章正文指出:

2.4内核上下文开关带来了一些小变化:不再推送/弹出EBX,但它现在包含在内联程序集的输出中。我们有一个新的输入参数

我不知道他们在哪里告诉编译器EAX、ECX和EDX不能存活,所以这很奇怪。这可能是一个bug,他们通过使函数
noinline
或其他什么来逃避

i386上的Linux 2.6使用了更多的输出操作数,使编译器能够处理保存/还原

但是Linux 2.6 for x86-64引入了一个技巧,可以轻松地将保存/恢复操作交给编译器:
\define\uu EXTRA\u CLOBBER,“rcx”、“rbx”、“rdx”、“r8”、“r9”、“r10”、“r11”、“r12”、“r13”、“r14”、“r15”

注意CLOBBER声明:
:“memory”,“cc”\uuuextra\uclobber

这告诉编译器内联asm将销毁所有这些寄存器,因此编译器将发出指令,在任何函数
switch_to
最终内联到的开始/结束处保存/恢复这些寄存器

告诉编译器在上下文切换后所有寄存器都会被销毁,解决了与使用内联asm手动保存/恢复寄存器相同的问题。编译器仍将生成一个遵循调用约定的函数


上下文开关切换到新任务的堆栈,因此编译器生成的保存/还原代码始终使用适当的堆栈指针运行。请注意,Linux 2.2和2.4内联asm中的显式push/pop指令在其他指令之前/之后。

@MichaelPetch:我看不到这一差异的修复方法。仍然没有
“=c”
“=d”
输出或clobber。我之前打开了太多链接,但我提供了错误的链接。我必须去搜索它的增益。您可以看到2.6.26-rc1中有一个是固定的:。上一个2.6.25版本仍然存在错误:如果你查看代码,你会看到有一个_set_base和_set_limit函数。看看内存操作数(它们被标记为已读,但会被写入)。直到(包括)上一个2.6.31.14版本,这种情况一直存在。在2.6.32中,对其进行了重构,并删除了这些组件的内联程序集。x86-64交换机也被巴斯德化了,但存在同样的碰撞问题。当所有的代码都被删除时,它最终被修复了(据我所知)refactored@MichaelPetch:哇,没有
“内存”
clobber真是太糟糕了。让人惊讶的是,有人把它放进了内核。我知道Linux有时在asm方面很草率,但这似乎比大多数情况更糟糕。