【问题标题】:Linux kernel code optimizationLinux内核代码优化
【发布时间】:2017-11-05 11:03:26
【问题描述】:

来自Understanding Linux Kernel 3rd edition在描述内核固定映射的章节中,有一个函数使用了以下枚举器-

每个固定映射的线性地址都由一个小整数索引表示 在枚举 fixed_addresses 数据结构中定义:

 enum fixed_addresses {
 FIX_HOLE,
 FIX_VSYSCALL,
 FIX_APIC_BASE,
 FIX_IO_APIC_BASE_0,
 [...]
 _ _end_of_fixed_addresses
 };

并给出这个枚举器,以下函数将编译 -

固定映射的线性地址放在第四个的末尾 千兆字节的线性地址。 fix_to_virt( ) 函数计算 从索引开始的常量线性地址:

inline unsigned long fix_to_virt(const unsigned int idx)
{
    if (idx >= _ _end_of_fixed_addresses)
        _ _this_fixmap_does_not_exist( );
    return (0xfffff000UL - (idx << PAGE_SHIFT));
}

现在,看看下面关于这个函数是如何最终被翻译成0xfffff000-(3 &lt;&lt; PAGE_SHIFT)的解释

假设某个内核函数调用 fix_to_virt(FIX_IOAPIC_BASE_0)。因为函数被声明为 “内联”,C 编译器不生成对 fix_to_virt( ) 的调用, 但在调用函数中插入其代码。此外,检查 索引值永远不会在运行时执行。实际上, FIX_IOAPIC_BASE_0 是一个等于 3 的常数,所以编译器可以剪切 去掉 if 语句,因为它的条件在编译时为假。 相反,如果条件为真或 fix_to_virt( ) 的参数 不是常量,编译器在链接期间发出错误 相位因为符号_ _this_fixmap_does_not_exist 没有在任何地方定义。最终,编译器计算 0xfffff000-(3

因此,代码的编写者似乎依赖于一些假设,即如果我们有一个 if 语句来比较在编译时定义的两个数字(比如说 FIX_IO_APIC_BASE_0 和 _ _end_of_fixed_addresses),因此结果在编译时知道,if 语句肯定会被优化并从代码中删除。

Linux 内核代码如何假设?

另外,编写此类代码的动机是什么?如果作者希望函数被评估为0xfffff000-(3 &lt;&lt; PAGE_SHIFT),为什么不直接写0xfffff000-(3 &lt;&lt; PAGE_SHIFT) 而不是调用这个函数呢?

【问题讨论】:

    标签: c linux compilation linux-kernel


    【解决方案1】:

    这里其实有两个问题:

    • Linux 内核代码如何假定代码已从二进制文件中删除?简单的答案是编译环境(编译器等)保证了这一点。基本上,常量传播是一种简单且众所周知的编译器优化,在这种情况下,它可以很容易地检测出未使用的代码。
    • 为什么不直接拼出代码呢?简单的原因是可移植性和可读性。可移植性意味着这允许您在一个地方针对新平台或 CPU 调整功能。可读性意味着意图与此函数的可能文档一起传达,如果您只是将原始函数内容转储在那里,情况就不会如此。这类似于在代码中不使用“幻数”的理由。

    【讨论】:

    • 好的,你能提供一份文件来支持你的第一点吗?我希望,如果 Linux 内核依赖于某些假设,那么该假设应该得到很好的定义和记录,并且没有解释或依赖编译器优化选项的空间
    • 过去的情况是,Linux 除了 GCC 之外什么都不编译,甚至不是它的每个版本,因此可移植到其他工具链并不是该项目的主要目标。另外,我找到了unix.stackexchange.com/questions/153788/…,这支持了我的主张。
    【解决方案2】:

    _ _end_of_fixed_addresses 在单元编译时是已知的,但不是 idx:该函数有可能从外部源代码中使用无效的 idx 调用。由于 this_fixmap_does_not_exists 符号不存在,就像解释中所说的那样,如果开发人员尝试使用带有无效 idx 的 fix_to_virt 将导致编译错误。但如果他使用正确的,'if' 语句将被编译器删除。因此,这实际上不是运行时保护,而是开发保护,一种将可能的运行时错误转换为编译错误的技术。关于修复结果,请注意这只是示例。 idx 可能与 3 不同,因此是该函数的结果。

    【讨论】:

    • 作为一个挑剔者,我建议称其为链接器错误。编译器错误将是使用“静态断言”的错误。通过这种更改,优先考虑编译器错误而不是链接器错误而不是运行时错误的规则更有意义。我不认为编译器错误可以在该代码中使用,但我不确定 GCC 是否没有像静态断言这样的东西。
    • 好吧,假设它会引发链接错误,但无论如何,它是一种将可能的运行时错误转换为设计时错误的方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-04
    • 1970-01-01
    • 2012-11-22
    • 2011-07-01
    相关资源
    最近更新 更多