我以为你不能在Linux内核中执行浮点运算
你不能safely: 无法使用kernel_fpu_begin()
/ kernel_fpu_end()
并不意味着 FPU 指令会出错(至少在 x86 上不会)。
相反,它会默默地破坏用户空间的 FPU 状态。这不好;不要那样做。
编译器不知道什么kernel_fpu_begin()
意味着,因此它无法检查/警告在 FPU 开始区域之外编译为 FPU 指令的代码。
可能有一种调试模式,内核会在外部禁用 SSE、x87 和 MMX 指令kernel_fpu_begin
/ end
区域,但这会比较慢并且默认情况下不会这样做。
不过,这是可能的:设置CR0::TS = 1
使 x87 指令出现故障,因此可以进行惰性 FPU 上下文切换,并且还有用于 SSE 和 AVX 的其他位。
有many有缺陷的内核代码导致严重问题的方法。这只是众多之一。在 C 中,您几乎总是知道何时使用浮点(除非拼写错误导致1.
常量或实际编译上下文中的某些内容)。
为什么 FP 架构状态与整数不同?
Linux 在每次进入/退出内核时都必须保存/恢复整数状态。所有代码都需要使用整数寄存器(除了以 FPU 计算结尾的巨大直线块)jmp
代替ret
(ret
修改rsp
).)
但内核代码通常会避免 FPU,因此 Linux 在系统调用进入时不保存 FPU 状态,仅在实际上下文切换到不同的上下文之前保存用户空间过程或kernel_fpu_begin
。否则,返回到同一内核上的同一用户空间进程是很常见的,因此不需要恢复 FPU 状态,因为内核没有触及它。 (如果内核任务确实修改了 FPU 状态,这就是发生损坏的地方。我认为这是双向的:用户空间也可能损坏yourFPU 状态)。
整数状态相当小,只有 16 个 64 位寄存器 + RFLAGS 和段寄存器。即使没有 AVX,FPU 状态也会大两倍以上:8 个 80 位 x87 寄存器,以及 16 个 XMM 或 YMM,或 32 个 ZMM 寄存器(+ MXCSR 和 x87 状态 + 控制字)。还有MPXbnd0-4
寄存器集中在“FPU”中。此时“FPU 状态”仅表示所有非整数寄存器。在我的天湖上,dmesg
says x86/fpu: Enabled xstate features 0x1f, context size is 960 bytes, using 'compacted' format.
See 了解Linux内核中FPU的使用;现代 Linux 默认情况下不会为上下文切换执行惰性 FPU 上下文切换(仅适用于内核/用户转换)。 (但那篇文章解释了 Lazy 是什么。)
大多数进程使用 SSE 在编译器生成的代码中复制/清零小内存块,并且大多数库 string/memcpy/memset 实现使用 SSE/SSE2。此外,硬件支持的优化保存/恢复现在已经成为一件事(xsaveopt/ xrstor),因此,如果某些/所有 FP 寄存器实际上尚未使用,“急切的”FPU 保存/恢复实际上可能会做更少的工作。例如仅保存 YMM 寄存器的低 128b(如果它们被清零)vzeroupper
所以CPU知道它们是干净的。 (并在保存格式中仅用一位来标记这一事实。)
通过“急切”的上下文切换,FPU 指令始终保持启用状态,因此不良的内核代码可能随时损坏它们。