首页 > 解决方案 > glibc 使用内核函数

问题描述

我试图从 glibc 源代码中理解这种方法:

    26 #if LIBM_SVID_COMPAT
    27 /* wrapper sqrtf */
    28 float
    29 __sqrtf (float x)
    30 {
    31   if (__builtin_expect (isless (x, 0.0f), 0) && _LIB_VERSION     != _IEEE_)
    32     return __kernel_standard_f (x, x, 126); /*    sqrt(negative) */
    33 
    34   return __ieee754_sqrtf (x);
    35 }
    36 libm_alias_float (__sqrt, sqrt)
    37 #endif

据我从这里了解到,如果程序没有sqrt从 glibc 中找到该函数的有效实现,它会从内核调用一个硬件函数,即一个特定的机器函数。这是正确的吗?
还有,什么libm_alias_float (__sqrt, sqrt)意思?

标签: c

解决方案


GNU C 库的数学函数完全在“glibc”源代码树中实现。他们不依赖操作系统内核做任何事情。在这种情况下,“内核”一词指的是计算内核——一些数学算法的核心,你可能想用手工优化的汇编语言编写的部分。

该函数__kernel_standard_f命名错误。它包含用于处理数学函数中的错误的通用代码;最好将其命名为__math_domain_error. 在这种情况下,当它的参数为负时__sqrtf调用;然后将负责设置为,可能会调用 SVID回调,并返回一个 NaN。(神秘代码 126 告诉它是哪个函数调用它以及为什么调用它。实现是在和。)__kernel_standard_f__kernel_standard_ferrnoEDOMmatherr__kernel_standard_f__kernel_standard_fsysdeps/ieee754/k_standardf.csysdeps/ieee754/k_standard.c

该条件_LIB_VERSION != _IEEE_与是否启用 POSIX 和/或 SVID 数学错误处理有关;现代数学算法实际上宁愿只在结果中查找 NaN 而不会让库浪费时间设置errno或调用matherr,因此有一种机制可以关闭后两者。

如果 to 的参数__sqrtf不是负数,它会尾部调用__ieee754_sqrtf这会执行平方根的实际计算。在 glibc 源代码树中有这个函数的几个替代实现;它的通用、仅限 C 的版本位于sysdeps/ieee754/flt-32/e_sqrtf.c. (即使按照 glibc 的标准,实现数学函数的文件的名称也很神秘。我不费心去理解它们,我只是做find sysdeps -name '*sqrtf*'或其他什么。)这个函数从逻辑开始,以检测否定参数(也是零,无穷大, 和 NaN) 并返回适当的值,但它不会触及errno. 如果您想了解用于计算平方根的数学技术,请查看此文件。

如果您自己运行上述find命令,您会发现其他几个名为e_sqrtf.c; 这些都在以特定 CPU 命名的目录中,其浮点单元具有计算平方根的硬件支持,例如sysdeps/x86_64/fpu/e_sqrt.c读取

double
__ieee754_sqrt (double x)
{
  double res;

  asm ("sqrtsd %1, %0" : "=x" (res) : "xm" (x));

  return res;
}

因为 x86sqrtsd指令完成了整个工作。

当为其中一个 CPU 构建时,Glibc 的构建过程将选择其中一个文件,而不是通用 C 版本。这种机制很复杂并且没有完整的文档记录,但是glibc 手册的“维护”附录,特别是它的“源代码布局”和“移植”部分,给出了一个不错的概述。


libm_alias_float (__sqrt, sqrt)安排__sqrtf也有名字sqrtf,只是作为一个弱别名。这对于符合标准是必要的。如果您想了解更多详细信息,请提出单独的问题。


推荐阅读