在 kernel_fpu_end 之前调用 kernel_fpu_begin 两次

标签 c linux linux-kernel linux-device-driver kernel-module

我在 asm/i387.h 中使用 kernel_fpu_beginkernel_fpu_end 函数保护 Linux 内核模块内部一些简单浮点运算的 FPU 寄存器状态。

我很好奇在 kernel_fpu_end 函数之前两次调用 kernel_fpu_begin 函数的行为,反之亦然。例如:

#include <asm/i387.h>

double foo(unsigned num){
    kernel_fpu_begin();

    double x = 3.14;
    x += num;

    kernel_fpu_end();

    return x;
}

...

kernel_fpu_begin();

double y = 1.23;
unsigned z = 42;
y -= foo(z);

kernel_fpu_end();

foo函数中,我调用了kernel_fpu_beginkernel_fpu_end;但是 kernel_fpu_begin 在调用 foo 之前已经被调用了。这会导致未定义的行为吗?

此外,我是否应该在 foo 函数中调用 kernel_fpu_end?我在 kernel_fpu_end 调用后返回了一个 double,这意味着访问浮点寄存器是不安全的,对吗?

我最初的猜测是不要在 foo 函数中使用 kernel_fpu_beginkernel_fpu_end 调用;但是,如果 foodouble 强制转换为 unsigned 会怎样——程序员不知道要使用 kernel_fpu_beginkernel_fpu_endfoo 之外?

最佳答案

简短回答:不,嵌套 kernel_fpu_begin() 是不正确的调用,这将导致用户空间 FPU 状态被破坏。

中等答案:这行不通,因为 kernel_fpu_begin()使用当前线程的 struct task_struct保存 FPU 状态( task_struct 有一个体系结构相关的成员 thread ,在 x86 上,thread.fpu 保存线程的 FPU 状态),然后做第二个 kernel_fpu_begin()将覆盖原来保存的状态。然后做 kernel_fpu_end()最终会恢复错误的 FPU 状态。

长答案:正如您在 <asm/i387.h> 中看到的实际实现一样,细节有点棘手。在较旧的内核中(如您查看的 3.2 源代码),FPU 处理始终是“惰性”的——内核希望避免在真正需要它之前重新加载 FPU 的开销,因为线程可能会运行并再次被调度无需实际使用 FPU 或需要其 FPU 状态。所以kernel_fpu_end()只是设置了 TS 标志,这会导致 FPU 的下一次访问陷入陷阱并导致 FPU 状态重新加载。希望我们实际上没有足够多的时间使用 FPU,从而使整体成本更低。

但是,如果您查看较新的内核(我相信是 3.7 或更新版本),您会发现所有这些实际上都有第二条代码路径——“eager”FPU。这是因为较新的 CPU 具有“优化的”XSAVEOPT 指令,并且较新的用户空间更频繁地使用 FPU(对于 memcpy 中的 SSE 等)。 XSAVEOPT/XRSTOR 的成本更低,惰性优化实际上避免 FPU 重新加载的机会也更少,因此在新 CPU 上使用新内核,kernel_fpu_end()继续并恢复 FPU 状态。 (

但是在“惰性”和“急切”FPU 模式下,task_struct 中仍然只有一个槽。保存 FPU 状态,所以嵌套 kernel_fpu_begin()最终会破坏用户空间的 FPU 状态。

关于在 kernel_fpu_end 之前调用 kernel_fpu_begin 两次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15934615/

相关文章:

c - 使用 Ubuntu 终端运行我的代码

C - 调用函数并给出二维数组

linux - 在cygwin中执行kinit时遇到 "fatal error - NtCreateEvent(lock): 0xC0000077"

java - ubuntu 上的 izpack 安装程序 'is not marked as executable'

android - __setup 宏不工作

c++ - 如何让一个整数对数组按两个整数排序,同时仍然在 C 中关联它们?

c - 如何计算自回归模型中的系数值?

java - 我想在 Linux shell 中通过 Java 执行的一个命令有问题

linux-kernel - Linux内核ARM异常栈init

android - "bitsperlong.h": trouble cross-building Perf for ARM (Android)