在描述内核固定映射的章节中的理解Linux内核第三版中,有一个函数使用以下enumorator-
每个固定映射的线性地址由枚举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 << PAGE_SHIFT)的解释
让我们假设某些内核函数调用fix_to_virt(FIX_IOAPIC_BASE_0)。因为函数声明为“内联”,所以C编译器不会生成对fix_to_virt( )的调用,而是将其代码插入调用函数中。此外,对索引值的检查从不在运行时执行。实际上,FIX_IOAPIC_BASE_0是等于3的常量,所以编译器可以删除if语句,因为它的条件在编译时为false。相反,如果条件为真或fix_to_virt( )的参数不是常量,编译器将在链接阶段发出错误,因为符号_ _this_fixmap_does_not_exist没有在任何地方定义。最后,编译器计算0xfffff 000-(3 << PAGE_SHIFT)并用常量线性地址0 call 000替换fix_to_virt( )函数调用。
因此,代码的编写者似乎依赖于这样的假设:如果我们有一个if语句,并比较在编译时定义的两个数字(比如FIX_IO_APIC_BASE_0和_ _end_of_fixed_addresses),那么结果是在编译时知道的,而if语句是,肯定是要优化并从代码中删除的。
Linux内核代码如何假定这一点?
此外,这类代码的动机是什么?如果作者希望将函数求值给0xfffff000-(3 << PAGE_SHIFT),为什么不直接编写0xfffff000-(3 << PAGE_SHIFT)而不是调用这个函数呢?
发布于 2017-11-05 11:16:19
这里实际上有两个问题:
发布于 2017-11-05 11:26:27
_ _end_of_fixed_addresses在单元编译时已知,但不知道idx:该函数有可能是从外部源代码中用无效的idx调用的。正如在解释中所告诉的,由于this_fixmap_does_not_exists符号不存在,如果开发人员试图将fix_to_virt与无效的idx一起使用,则会出现编译错误。但是如果他使用正确的语句,“if”语句将被编译器删除。因此,这实际上不是一种运行时保护,而是一种开发保护,一种将可能的运行时错误转换为编译错误的技术。关于修复结果,请注意,这仅仅是一个例子。idx可以是不同的3,所以这个函数的结果。
https://stackoverflow.com/questions/47120814
复制相似问题