浮点 API

内核代码通常禁止使用浮点 (FP) 寄存器或指令,包括 C 语言的 float 和 double 数据类型。此规则减少了系统调用开销,因为内核无需保存和恢复用户空间的浮点寄存器状态。

然而,偶尔驱动程序或库函数可能需要包含浮点代码。通过将包含浮点代码的函数隔离到单独的翻译单元(单独的源文件),并在调用这些函数时保存/恢复浮点寄存器状态来支持此功能。这创建了浮点使用的“临界区”。

进行这种隔离的原因是为了防止编译器在这些临界区之外生成涉及浮点寄存器的代码。编译器有时会使用浮点寄存器来优化内联的 memcpy 或变量赋值,因为浮点寄存器可能比通用寄存器更宽。

浮点代码在内核中的可用性是架构特定的。此外,由于单个内核可能被配置为同时支持带有和不带有浮点单元的平台,因此必须在构建时和运行时都检查 FPU 的可用性。

如以下所述,有几种架构实现了 linux/fpu.h 中的通用内核浮点 API。其他一些架构实现了自己独特的 API,这些 API 已单独文档化。

构建时 API

如果选项 ARCH_HAS_KERNEL_FPU_SUPPORT 被启用,则可以构建浮点代码。对于 C 代码,此类代码必须放在单独的文件中,并且该文件必须使用以下模式调整其编译标志

CFLAGS_foo.o += $(CC_FLAGS_FPU)
CFLAGS_REMOVE_foo.o += $(CC_FLAGS_NO_FPU)

架构应根据需要在其顶层 Makefile 中定义这些变量中的一个或两个。例如

CC_FLAGS_FPU := -mhard-float

CC_FLAGS_NO_FPU := -msoft-float

常规内核代码假定使用与 CC_FLAGS_NO_FPU 等效的标志。

运行时 API

运行时 API 在 linux/fpu.h 中提供。此头文件不能从实现浮点代码的文件(那些编译标志已如上调整的文件)中包含。相反,它必须在定义浮点临界区时包含。

bool kernel_fpu_available(void)

此函数报告浮点代码是否可在当前 CPU 或平台上使用。此函数返回的值在运行时不应改变,因此只需调用一次,而不是在每个临界区之前调用。

void kernel_fpu_begin(void)
void kernel_fpu_end(void)

这些函数创建了一个浮点临界区。只有在先前调用 kernel_fpu_available() 返回 true 后,调用 kernel_fpu_begin() 才有效。这些函数只保证可从(可抢占或不可抢占的)进程上下文调用。

在临界区内部,抢占可能会被禁用,因此它们的尺寸应尽量减小。它们要求是可重入的。如果调用方期望嵌套临界区,则必须实现自己的引用计数。