为什么 Linux 内核没有像 glibc 那样优化功能(例如,memchr、strchr)?

Win*_*oon 25 libraries glibc linux-kernel

当我尝试自己实现C字符串库时,发现glibc和Linux内核在实现某些功能的方式上有所不同。例如,glibc memchrglibc strchr使用一些技巧来加速函数,但内核 memchr内核 strchr没有。为什么 Linux 内核函数没有像 glibc 那样优化?

Ste*_*itt 41

内核在特定于 arch 的目录中确实有一些这些功能的优化版本;参见例如x86 实现memchr(参见所有memchr定义所有strchr定义)。您找到的版本是后备通用版本;你可以通过寻找保护检查来发现这些,#ifndef __HAVE_ARCH_MEMCHRformemchr#ifndef __HAVE_ARCH_STRCHRfor strchr

C 库的优化版本确实倾向于使用更复杂的代码,因此上述内容并没有解释为什么内核不竭尽全力加快速度。如果您能找到内核可以从这些函数之一的更优化版本中受益的场景,我想一个补丁会受到欢迎(有适当的支持证据,只要优化的函数仍然可以理解——请参阅关于memcpy)。但我怀疑内核对这些函数的使用通常不值得;例如memcpy,相关函数往往用于内核中的小缓冲区。并且永远不要低估适合缓存或可以内联的短函数的速度收益......

此外,正如Iwillnotexist Idonotexist所提到的,MMX 和 SSE 不能轻易在内核中使用,许多优化版本的内存搜索或复制功能都依赖于它们。

在许多情况下,无论如何,所使用的版本最终都是编译器的内置版本,并且这些版本都经过了大量优化,甚至比 C 库的要优化得多(例如,memcpy通常会转换为寄存器加载和存储,或甚至是一个不变的商店)。

  • 许多函数的内核版本,可能包括 `*chr()`,也可能故意避免使用向量寄存器,因为它需要启用它们和/或保存它们的状态,而这可能尚未完成和/或可能会增加延迟。 (10认同)
  • 请注意,在某些情况下,为了实现性能以外的目标,这些功能可能有意降低性能(例如,提到的二进制大小,以及针对侧信道攻击的时间/安全性、可移植性等),因此只需拉一下由于这些原因,请求“这性能更高(在 arch X 上)”可能会被拒绝。 (9认同)
  • 另一件事:按照 C 标准,glibc 函数在技术上是非法代码,因为它们包含故意读入缓冲区溢出(由于它们以 4 字节块读取的事实,然后考虑如果字符串长度为不是 4) 的倍数,根据 C 标准,这是未定义行为 (UB),但因为这是编译器,所以可以“逃脱”它,因为可以对其进行编程以使其滑动 - 实际上,形成一个非标准“方言”,专门用于以高性能方式编写自己的标准设施库。 (3认同)
  • @The_Sympathizer Linux 使用相同的技巧;它并不意味着能够使用任何编译器,只能使用具有特定标志的 GCC。 (2认同)

sch*_*ily 7

我记得我必须在 2006 年修复 Solaris 中的内核核心转储错误,该错误是由mkisofs.

该 ISO 格式化软件没有在 ISO-9660 目录条目的中间包含 Rock Ridge 文件名(由 完成mkisofs),而是在 ISO-9660 目录条目的末尾。现在您需要知道 Rock Ridge 文件名不会以空字节结尾...

发生的情况是 Solaris 内核中的(当时过于优化)字符串例程在某些情况下可能会超出 1,并且如果 Rock Ridge 文件名恰好在 2k 扇区的末尾结束并且该扇区刚好在末尾结束对于 4k 内核内存页面,这种超调访问会由于非法内存访问而导致内核崩溃。

我们需要将访问代码重写为非常保守,以防止将来发生这种内核恐慌。

如您所见,有时为内核编写安全代码要困难得多,并且有时为了避免内核恐慌,此类代码会稍微慢一些。

顺便说一句:如果有可能到达 MMU 页面的末尾,可以通过让链接器在段后添加几个字节来处理用户空间程序中从 CPU 中潜在不可预测的预取的问题。这在依赖于 om mmaping 区域的内核中不起作用。