首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在kernel_fpu_begin之前调用kernel_fpu_end两次

在kernel_fpu_begin之前调用kernel_fpu_end两次
EN

Stack Overflow用户
提问于 2013-04-10 19:22:14
回答 3查看 3.3K关注 0票数 2

我使用kernel_fpu_beginkernel_fpu_end函数来保护asm/I 387.h内核模块中的一些简单浮点算法的FPU寄存器状态。

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

代码语言:javascript
复制
#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;但是在调用foo之前已经调用了kernel_fpu_begin。这会导致不明确的行为吗?

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

我最初的猜测是,在kernel_fpu_begin函数中不使用fookernel_fpu_end调用;但是,如果foodouble强制转换为未签名的 --程序员不会知道在foo之外使用kernel_fpu_beginkernel_fpu_end

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-04-17 19:09:00

简单回答:不,嵌套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或更高版本),您将看到实际上有第二条代码路径用于所有这些--“急切”的FPU。这是因为较新的FPU具有“优化”的XSAVEOPT指令,较新的用户空间更频繁地使用FPU(用于memcpy中的SSE,等等)。XSAVEOPT / XRSTOR的成本较低,而延迟优化实际上避免FPU重新加载的可能性也较小,因此在新的CPU上有一个新内核时,kernel_fpu_end()就会继续恢复FPU状态。(

然而,在“懒惰”和“急切”的FPU模式中,task_struct中仍然只有一个槽来保存FPU状态,因此嵌套kernel_fpu_begin()将最终破坏用户空间的FPU状态。

票数 7
EN

Stack Overflow用户

发布于 2013-04-15 19:02:15

我正在用我所理解的asm/I 387.h Linux源代码(版本3.2)进行注释。

代码语言:javascript
复制
static inline void kernel_fpu_begin(void)
{
        /* get thread_info structure for current thread */
        struct thread_info *me = current_thread_info();

        /* preempt_count is incremented by 1
         * (preempt_count > 0 disables preemption,
         *  while preempt_count < 0 signifies a bug) */
        preempt_disable();

        /* check if FPU has been used before by this thread */
        if (me->status & TS_USEDFPU)
                /* save the FPU state to prevent clobbering of
                 * FPU registers, then reset the TS_USEDFPU flag */
                __save_init_fpu(me->task);
        else
                /* clear the CR0.TS bit to prevent
                 * unnecessary FPU task context saving */
                clts();
}

static inline void kernel_fpu_end(void)
{
        /* set CR0.TS bit (signifying the processor switched
         * to a new task) to enable FPU task context saving */
        stts();

        /* attempt to re-enable preemption
         * (preempt_count is decremented by 1);
         * reschedule thread if needed
         * (thread will not be preempted if preempt_count != 0) */
        preempt_enable();
}

FXSAVE指令通常用于保存FPU状态。但是,我相信每次在同一个线程中调用kernel_fpu_begin时,内存目的地都保持不变;不幸的是,这意味着FXSAVE将覆盖先前保存的FPU状态。

因此,我怀疑不能安全地嵌套kernel_fpu_begin调用。

但是,我仍然无法理解的是如何恢复FPU状态,因为kernel_fpu_end调用似乎没有执行FXRSTOR指令。另外,如果我们不再使用FPU,为什么在CR0.TS调用中设置kernel_fpu_end位呢?

票数 0
EN

Stack Overflow用户

发布于 2013-04-11 06:47:32

是的,正如您定义的一些双变量& foo也返回了double值;您必须在foo 还有。之外使用kernel_fpu_beginkernel_fpu_end调用

相似问题也有一些实例,您可以在这些实例中不使用kernel_fpu_beginkernel_fpu_end调用来编写代码。

票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15934615

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档