[转]Linux 内核使用浮点问题

2020-09-12  本文已影响0人  Nothing_655f

一、硬浮点与软浮点

1. 硬浮点

编译器将代码直接编译成硬件浮点协处理器(浮点运算单元FPU)能识别的指令,这些指令在执行的时候ARM核直接把它转给协处理器执行。FPU 通常有一套额外的寄存器来完成浮点参数传递和运算。使用实际的硬件浮点运算单元(FPU)会带来性能的提升

2. 软浮点

编译器把浮点运算转成浮点运算的函数调用和库函数调用(即用整数运算模拟浮点运算),没有FPU的指令调用,也没有浮点寄存器的参数传递。浮点参数的传递也是通过ARM寄存器或者堆栈完成。现在的Linux系统默认编译选择使用hard-float,如果系统没有任何浮点处理器单元,这就会产生非法指令和异常。因而一般的系统镜像都采用软浮点以兼容没有VFP的处理器。

3. ARM软浮点与硬浮点编译

出于低功耗、封装限制等种种原因,以前的一些ARM处理器没有独立的硬件浮点运算单元,需要用软件来实现浮点运算。随着技术发展,现在高端的ARM处理器基本都具备了硬件执行浮点操作的能力。 新旧两种架构之间的差异,就产生了两个不同的接口 – 软浮点与硬浮点。

编译选项:

-mfpu =name(neon or vfp)指定FPU 单元

-mfloat-abi= name(soft、hard、 softfp):指定软件浮点或硬件浮点或 兼容软浮点调用接口

-mfpu可以指定使用FPU单元类型,以ARM A76为例有两种:VFP和Neon

VFP与Neon

soft、hard、 softfp

在有fpu的情况下,可以通过-mfloat-abi来指定使用哪种,有如下三种值:

而arm64,64位的arm默认就是hard float的,因此不需要hf的后缀。kernel、rootfs和app编译的时候,指定的必须保持一致才行。

使用softfp模式,会存在不必要的浮点到整数、整数到浮点的转换。而使用hard模式,在每次浮点相关函数调用时,平均能节省20个CPU周期。

4. ARM启用硬浮点运算

需要使用硬浮点,需要内核开启对硬浮点支持,同时编译时要使用上面的softfp或hard,才可以使用FPU/Neon进行计算。

二、 在内核代码中使用浮点问题

1. ARM64的用户空间进程切换 ---- 浮点寄存器切换

ARM64,默认使用的是硬浮点,在进程切换时会涉及对浮点寄存器的操作。

对于ARM64而言,进程切换的context包括:

(1)通用寄存器

(2)浮点寄存器

(3)地址空间寄存器(ttbr0_el1和ttbr1_el1)

(4)其他寄存器(ASID、thread process ID register等)

Path:arch/arm64/kernel/process.c
/*
 * Thread switching.
 */
__notrace_funcgraph struct task_struct *__switch_to(struct task_struct *prev,
                struct task_struct *next)
{
    struct task_struct *last;

    fpsimd_thread_switch(next); 
    /*fp是float-point的意思,和浮点运算相关。fpsimd_thread_switch
    其实就是把当前FPSIMD的状态保存到了内存中(task.thread.fpsimd_state),
    从要切入的next进程描述符中获取FPSIMD状态,并加载到CPU上。*/
    tls_thread_switch(next);
    hw_breakpoint_thread_switch(next);
    contextidr_thread_switch(next);
    ........
    return last;
}
---------------------------------------
Path:arch/arm64/kernel/fpsimd.c
void fpsimd_thread_switch(struct task_struct *next)
{
    ......
    /* Save unsaved fpsimd state, if any: */
    fpsimd_save(); //这里将struct fpsimd_state保存到通用寄存器X0中
    ......
}
-----------------------------------------
Path:arch/arm64/kernel/entry-fpsimd.S
/* Save the FP registers. x0 - pointer to struct fpsimd_state*/
ENTRY(fpsimd_save_state)
    fpsimd_save x0, 8
    ret
ENDPROC(fpsimd_save_state)
1234567891011121314151617181920212223242526272829303132333435

2. 内核使用浮点

关于内核使用浮点, Robert Love’s 《Linux Kernel Development》(Linux内核设计与实现)书中提到

No (Easy) Use of Floating Point

When a user-space process uses floating-point instructions, the kernel manages the transition from integer to floating point mode. What the kernel has to do when using floating-point instructions varies by architecture, but the kernel normally catches a trap and then initiates the transition from integer to floating point mode.

Unlike user-space, the kernel does not have the luxury of seamless support for floating point because it cannot easily trap itself. Using a floating point inside the kernel requires manually saving and restoring the floating point registers, among other possible chores. The short answer is: Don’t do it! Except in the rare cases, no floating-point operations are in the kernel.

后面这句话提到如果内核使用浮点则需要保存恢复浮点寄存器等其他杂项,这样会导致内核性能下降,所以一般不建议使用浮点,除非特殊情况。

但是Robert Love’s在这里并没有讨论如何在内核中正确使用浮点,以及未正确使用浮点可能会出现什么问题。当你需要在内核中使用浮点,如果按照用户空间的写法可能会出现一些意想不到的情况。如:程序Crash,内存越界、访问非法内存地址,浮点计算出错等稀奇古怪的问题。出现这种问题的原因是:内核由于性能原因,在内核运行的代码,内核在进行上下文切换时,不会主动保存和恢复浮点寄存器。这样可能会导致内核在进行浮点运算时,可能会破坏此时用户空间的浮点寄存器状态,导致用户空间的fpsimd_state状态异常,随后程序的行为将变的不可控。

在这里插入图片描述

参考文章:

ARM 浮点运算,软浮点,硬浮点

ARM Cortex-A8: Whats the difference between VFP and NEON

armel、armhf和arm64区别选择

ARM64 汇编——寄存器和指令

ARMv8-aarch64寄存器和指令集

进程切换分析(1):基本框架

linux kernel态下使用NEON对算法进行加速

转载自 Linux 内核使用浮点问题

上一篇 下一篇

猜你喜欢

热点阅读