我使用kernel_fpu_begin和kernel_fpu_end函数来保护asm/I 387.h内核模块中的一些简单浮点算法的FPU寄存器状态。
我很好奇在kernel_fpu_begin函数之前两次调用kernel_fpu_end函数的行为,反之亦然。例如:
#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_begin和kernel_fpu_end;但是在调用foo之前已经调用了kernel_fpu_begin。这会导致不明确的行为吗?
此外,我甚至应该在kernel_fpu_end函数中调用foo吗?我在调用之后返回一个kernel_fpu_end双,这意味着访问浮点寄存器是不安全的,对吗?
我最初的猜测是,在kernel_fpu_begin函数中不使用foo和kernel_fpu_end调用;但是,如果foo将double强制转换为未签名的 --程序员不会知道在foo之外使用kernel_fpu_begin和kernel_fpu_end。
发布于 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状态。
发布于 2013-04-15 19:02:15
我正在用我所理解的asm/I 387.h 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();
}FXSAVE指令通常用于保存FPU状态。但是,我相信每次在同一个线程中调用kernel_fpu_begin时,内存目的地都保持不变;不幸的是,这意味着FXSAVE将覆盖先前保存的FPU状态。
因此,我怀疑不能安全地嵌套kernel_fpu_begin调用。
但是,我仍然无法理解的是如何恢复FPU状态,因为kernel_fpu_end调用似乎没有执行FXRSTOR指令。另外,如果我们不再使用FPU,为什么在CR0.TS调用中设置kernel_fpu_end位呢?
发布于 2013-04-11 06:47:32
是的,正如您定义的一些双变量& foo也返回了double值;您必须在foo 还有。之外使用kernel_fpu_begin和kernel_fpu_end调用
相似问题也有一些实例,您可以在这些实例中不使用kernel_fpu_begin和kernel_fpu_end调用来编写代码。
https://stackoverflow.com/questions/15934615
复制相似问题