当内核开发人员要编写汇编时,他们是编写高层并使用编译器转换还是直接编写汇编?

When kernel developers want to write assembly, do they write in high level and convert it using compiler or they write in assembly directly?

我读到,例如 Linux 的某些部分在汇编中,我猜他们用汇编编写是为了更快的执行速度

但是现代内核开发人员实际上是在需要时直接用汇编编写,还是他们用高级语言编写并使用编译器将其转换为汇编,然后使用转换后的汇编代码?

哪种方法更好?考虑到编译器也有代码优化,将高级转换为汇编不是更有效吗?我真正需要用汇编编写内核的哪些部分?

为了性能,Linux 的极少数部分是用 asm 编写的。请参阅@Ped7g 的回答,了解更多关于为什么内核使用内联汇编来处理偶尔的特权指令(如 mov to/from 控制寄存器)或整个手写汇编文件作为入口点(如中断 和分派到 C 函数的系统调用处理程序入口点)。


在 Linux 中,也许只是 RAID5 异或奇偶校验(在 x86 上使用 SSE2 或 AVX)和 RAID6 纠错是用 asm 编写的以提高性能。

这些可能是直接用 asm 编写的,因为在 C 中使用内部函数手动矢量化并不容易。在那些 Linux 函数中,循环仍然是用 C 完成的,IIRC。

(而且它使用了非常糟糕的风格,有多个单独的 asm("") 语句使用 XMM 或 YMM 寄存器。这恰好有效,特别是在编译器永远不会生成使用 XMM 寄存器的代码的内核代码中, 但使用单个 asm 块或向量 output/input 操作数会更安全。请参阅 Linux's lib/raid6/sse2.c for an example. There's also asm/xor.h ,其中有一些通用的块异或函数,循环在 asm 中完成,也可能被其他部分使用内核。)这是它使用 SIMD 向量寄存器的少数几个地方之一,因为 saving/restoring FPU 状态很昂贵。

Linux 可能使用内联汇编来提高 x86 CRC32 指令的性能(如果可用);有几件事使用 x86 加速的 CRC32C 多项式。


对于您的问题的更一般情况,使用编译器生成的 asm 作为优化的起点通常是一个好主意

但是,如果编译器已经生成了良好的 asm,则您无需执行任何操作,只需使用该 C。这甚至比内联 asm 更好,因为它可以通过常量传播等进行优化。或者您可以调整 C 源代码以帮助编译器更有效地完成工作。

但是,如果您无法让编译器生成最佳循环,那么请确保您可以使用它的 asm 并手动优化它。只要对标原版,就不能输给编译器。 (除非您的 asm 因内联使某些内容成为编译时常量而无法优化。)

有关帮助与击败编译器的更多详细信息,请参阅

您只会考虑对软件的非常关键部分使用手写的 asm 循环,尤其是在像 Linux 这样的可移植代码库中,因为您需要针对每个平台使用不同的实现.

并且因为在 Skylake 上最佳的不是 20 年前在 P5 Pentium 上的最佳,并且在 20 年后的某些未来 x86 上可能不是最佳的。坚持使用可移植的 C 可以让像 -march=skylake 这样的调整选项发挥作用,并使 asm 为您正在编译的特定微体系结构进行调整。 (或者让编译器默认调整中的更新在多年后生效。)

更不用说大多数内核开发人员都不是 asm 调优专家,他们可以轻松地手动编写接近最优的 asm。这不是人们经常做的事情。如果您喜欢这样做,请使用 gcc 或 clang 使它们从 C 生成更优化的代码。

I read that for example some parts of Linux is in assembly, and i guess they write in assembly for faster speed of execution

通常不会,他们用汇编编写内核的[微小]部分,因为 C 语言不支持某些操作(例如,在 x86 CPU 上切换到保护模式需要写入 C 语言不知道的寄存器) .

再一次,C 非常适合内核之类的东西(它有点像 "low level" 语言,虽然我编程的时间越长,这些类别对我来说就越混乱,此时我相信最高抽象级别的编程语言之一实际上是 C++,但很多人不同意我的看法,但如果需要的话,你可以很容易地在 C++ 中获得相当低的级别),所以大部分的东西都可以直接用它来写,它只是影响目标机器某些特定事物的非常小的部分,必须用 asm 代码完成。

例如考虑内存管理器之类的东西..关于它的大部分内容(跟踪free/allocated页面,不同进程的虚拟内存映射等......)都是普通数据结构中的普通数字,并且可以在 C 中轻松处理。但是为特定进程设置最终虚拟内存布局可能需要不同的指令,具体取决于目标机器及其 MMU 设计,因此可能有一小部分程序集强制执行在 C 中计算的那些东西。