Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/65.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 调用内核\u fpu\u在内核\u fpu\u结束之前开始两次_C_Linux_Linux Kernel_Linux Device Driver_Kernel Module - Fatal编程技术网

C 调用内核\u fpu\u在内核\u fpu\u结束之前开始两次

C 调用内核\u fpu\u在内核\u fpu\u结束之前开始两次,c,linux,linux-kernel,linux-device-driver,kernel-module,C,Linux,Linux Kernel,Linux Device Driver,Kernel Module,我正在使用中的kernel\u fpu\u begin和kernel\u fpu\u end函数为Linux内核模块中的一些简单浮点算法保护fpu寄存器状态 我很好奇在调用kernel\u fpu\u end函数之前调用kernel\u fpu\u begin函数两次的行为,反之亦然。例如: #include <asm/i387.h> double foo(unsigned num){ kernel_fpu_begin(); double x = 3.14;

我正在使用中的
kernel\u fpu\u begin
kernel\u fpu\u end
函数为Linux内核模块中的一些简单浮点算法保护fpu寄存器状态

我很好奇在调用
kernel\u fpu\u end
函数之前调用
kernel\u fpu\u 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(无符号num){
内核_fpu_begin();
双x=3.14;
x+=num;
内核_fpu_end();
返回x;
}
...
内核_fpu_begin();
双y=1.23;
无符号z=42;
y-=foo(z);
内核_fpu_end();
foo
函数中,我调用
kernel\u fpu\u begin
kernel\u fpu\u end
;但是在调用
foo
之前已经调用了
kernel\u fpu\u begin
。这会导致未定义的行为吗

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


我最初的猜测是不要在
foo
函数中使用
kernel\u fpu\u begin
kernel\u fpu\u end
调用;但是如果
foo
返回了double转换为unsigned——程序员不知道使用
kernel\u fpu\u begin
kernel\u fpu\u end
之外的
foo

是的,正如您定义的一些双变量一样&
foo
也在返回双值;您必须使用
kernel\u fpu\u begin
kernel\u fpu\u end
调用
foo


还有一些例子,在这些例子中,您可以不使用
kernel\u fpu\u begin
kernel\u fpu\u end
调用来编写代码。

我正在用我理解的发生的事情来评论Linux源代码(版本3.2)

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();
}
静态内联void内核\u fpu\u开始(void)
{
/*获取当前线程的线程信息结构*/
struct thread_info*me=current_thread_info();
/*抢占计数增加1
*(抢占\u计数>0将禁用抢占,
*而抢占(U计数<0表示存在错误)*/
抢占禁用();
/*检查此线程以前是否使用过FPU*/
如果(me->status&T\u USEDFPU)
/*保存FPU状态以防止碰撞
*FPU寄存器,然后重置TS_USEDFPU标志*/
__保存初始fpu(me->task);
其他的
/*清除CR0.TS位以防止
*不必要的FPU任务上下文保存*/
clts();
}
静态内联void内核\u fpu\u结束(void)
{
/*设置CR0.TS位(表示处理器已切换
*添加到新任务)以启用FPU任务上下文保存*/
stts();
/*尝试重新启用抢占
*(抢占计数递减1);
*如果需要,重新调度线程
*(如果抢占计数!=0,线程将不会被抢占)*/
抢占启用();
}
FXSAVE
指令通常用于保存FPU状态。但是,我相信每次在同一线程中调用
kernel\u fpu\u begin
时,内存目标都保持不变;不幸的是,这意味着
FXSAVE
将覆盖以前保存的FPU状态

因此,我怀疑您不能安全地嵌套
kernel\u fpu\u begin
调用


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

简短回答:不,嵌套
内核fpu\u begin()
调用是不正确的,它将导致用户空间fpu状态损坏

中等回答:这不起作用,因为
kernel\u fpu\u begin()
使用当前线程的
struct task\u struct
保存fpu状态(
task\u struct
有一个依赖于体系结构的成员
thread
,在x86上,
thread.fpu
保存线程的fpu状态),并执行第二个
kernel\u\u\u begin()
将覆盖原始保存状态。然后执行
kernel\u fpu\u end()
将恢复错误的fpu状态

详细回答:正如您在
中看到的实际实现,细节有点棘手。在较旧的内核(如您看到的3.2源代码)中,FPU处理总是“懒惰的”--内核希望避免在真正需要FPU之前重新加载FPU的开销,因为线程可能会再次运行并被调度,而不需要实际使用FPU或需要其FPU状态。因此
kernel\u FPU\u end()
只需设置TS标志,这会导致FPU的下一次访问陷井并导致FPU状态重新加载。希望我们实际使用FPU的时间不会太长,这样总体上会更便宜

然而,如果你看一看较新的内核(我相信是3.7或更高版本),你会发现实际上还有第二个代码路径——急切的FPU。这是因为较新的CPU有“优化”的XSAVEOPT指令,而较新的用户空间更经常地使用FPU(对于memcpy中的SSE,等等)。XSAVEOPT/XRSTOR的成本更低,而且延迟优化实际上避免FPU重新加载的可能性也更小,因此在新CPU上安装新内核时,
kernel\u FPU\u end()
只需继续并恢复FPU状态(

然而,在“惰性”和“急切”FPU模式下,
任务结构中仍然只有一个插槽保存FPU状态,因此嵌套
内核\u FPU\u begin()
将最终损坏用户空间的FPU状态