Vil*_*ray 12 c linux x86 gcc linux-kernel
我正在使用x86 CentOS 6.3(内核v2.6.32)系统.
我将以下函数编译成一个简单的字符驱动程序模块作为实验,以了解Linux内核如何对浮点运算做出反应.
static unsigned floatstuff(void){
float x = 3.14;
x *= 2.5;
return x;
}
...
printk(KERN_INFO "x: %u", x);
Run Code Online (Sandbox Code Playgroud)
编译的代码(这是没有预料到的)所以我插入了模块并检查了日志dmesg.日志显示:x: 7.
这看起来很奇怪; 我以为你不能在Linux内核中执行浮点运算 - 除了一些例外kernel_fpu_begin().模块是如何执行浮点运算的?
这是因为我在x86处理器上吗?
不要那样做!
由于以下几个原因,在内核空间 FPU 模式被禁用:
如果你真的想使用 FP 数字(你不应该),你必须使用kernel_fpu_begin和kernel_fpu_end原语来避免破坏用户空间寄存器,并且你应该考虑在处理 FP 数字时所有可能的问题(包括安全性)。
我以为你无法在Linux内核中执行浮点运算
您无法安全:无法使用kernel_fpu_begin()/ kernel_fpu_end()并不意味着FPU指令会出错(至少不在x86上).
相反,它会默默地破坏用户空间的FPU状态.这是不好的; 不要那样做.
编译器不知道是什么kernel_fpu_begin()意思,因此无法检查/警告编译到FPU-begin区域之外的FPU指令的代码.
可能存在调试模式,其中内核在kernel_fpu_begin/ end区域之外禁用SSE,x87和MMX指令,但这样做会更慢并且默认情况下不会完成.
但是有可能:设置CR0::TS = 1使x87指令失败,因此可以进行延迟的FPU上下文切换,并且SSE和AVX还有其他位.
有缺陷的内核代码有很多方法可以导致严重的问题.这只是其中之一.在C中,您几乎总是知道何时使用浮点(除非拼写错误导致1.常量或实际编译的上下文中的某些内容).
为什么FP架构状态与整数不同?
Linux必须在进入/退出内核时保存/恢复整数状态.所有代码都需要使用整数寄存器(FPU计算的一个巨大的直线块除外,jmp而不是a ret(ret修改rsp).)
但是内核代码通常会避免使用FPU,因此Linux在系统调用进入时保留FPU状态,仅在实际上下文切换到不同的用户空间进程之前保存kernel_fpu_begin.否则,在同一核心上返回相同的用户空间进程是很常见的,因此不需要恢复FPU状态,因为内核没有触及它.(如果内核任务确实修改了FPU状态,那么这就是腐败发生的地方.我认为这有两个方面:用户空间也可能破坏你的 FPU状态).
整数状态相当小,只有16x 64位寄存器+ RFLAGS和段寄存器.即使没有AVX,FPU状态也是两倍大:8x 80位x87寄存器,16x XMM或YMM,或32x ZMM寄存器(+ MXCSR,x87状态+控制字).MPX bnd0-4寄存器也与"FPU"混为一谈.此时"FPU状态"仅表示所有非整数寄存器.在我的Skylake,dmesg说x86/fpu: Enabled xstate features 0x1f, context size is 960 bytes, using 'compacted' format.
请参阅了解Linux内核中的FPU用法 ; 现代Linux默认情况下不为上下文切换执行惰性FPU上下文切换(仅适用于内核/用户转换).(但那篇文章解释了Lazy是什么.)
大多数进程使用SSE在编译器生成的代码中复制/清零小块内存,大多数库字符串/ memcpy/memset实现使用SSE/SSE2.此外,硬件支持优化保存/恢复是现在的事情(xsaveopt/ xrstor),因此如果实际上没有使用某些/所有FP寄存器,那么"急切"FPU保存/恢复实际上可以做更少的工作.例如,如果它们被归零,只保存低128b的YMM寄存器,vzeroupper因此CPU知道它们是干净的.(并在保存格式中仅用一位标记该事实.)
通过"急切"上下文切换,FPU指令始终保持启用状态,因此糟糕的内核代码可能随时损坏它们.
| 归档时间: |
|
| 查看次数: |
6874 次 |
| 最近记录: |