浮点数 API

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

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

这种隔离的原因是为了防止编译器在这些临界区之外生成触摸 FP 寄存器的代码。编译器有时会使用 FP 寄存器来优化内联的 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 中提供。此头文件不能包含在实现 FP 代码的文件中(那些编译标志如上所述调整过的文件)。相反,它必须在定义 FP 临界区时包含。

bool kernel_fpu_available(void)

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

void kernel_fpu_begin(void)
void kernel_fpu_end(void)

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

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