改变模型编程。这个怎么运作?

时间:2015-01-18 17:37:05

标签: assembly cpu hardware

请解释一下。如果我知道每个CPU都有不同的编程模型。让我们假设,我有可以执行XYZ汇编指令的CPU。然后我将编译程序并将其发送给我的朋友,其中有没有XYZ程序集指令的CPU。尽管如此,我认为该计划将有效。如何编译程序一般独立于硬件架构?

1 个答案:

答案 0 :(得分:3)

当然二进制文件只能在它编译的体系结构的机器上执行。例如,您无法在x86上运行ARM可执行文件。大多数情况下,程序加载器甚至不会让你尝试。

我们能够成功分发二进制版本的软件(对于特定的体系结构,如x86-64),因为处理器必须支持一组基本指令。您的Intel CPU和您的伙伴的AMD CPU仍然实现相同的基本指令集。默认情况下,GCC只会发出属于此类别的说明。

此外,可以允许GCC发出更多专用指令,这些指令属于特定指令集扩展。例如,SSE扩展有各种迭代,它们执行向量数学运算。因为并非所有CPU都实现这些指令,所以必须明确告诉GCC允许它们使用它们。您可以使用-march-mtune标志向GCC执行此操作。 -march=native将编译一个程序以适合您的计算机,但如果您尝试在另一台具有不同CPU的计算机上运行相同的二进制文件,则可能无效。

虽然如果要编译代码,该策略仍然有效,但我们可以实现一种混合方法。这涉及使用CPUID指令来确定(在运行时)当前CPU上可用的哪些特征。然后,基于该信息,我们可以调用使用不同指令集扩展的不同函数。这些通常是在汇编中手工编码,或者在C中使用built-ins用于相关架构。

Linux内核实际上也是在运行时执行此操作,尽管通常以更令人印象深刻的方式。关于x86的几个例子:

  • SMP系统中,某些指令需要x86 LOCK前缀,以防止多个CPU同时修改相同的变量。然而,在单处理器(UP)系统上,该指令是不必要的并且减慢了速度。如果为SMP编译的内核确定它在UP系统上运行,它实际上将NOP - 输出LOCK指令,使其无效。也就是说,它在顶部写了一条“无操作”指令。

  • 可以为字符串指令提供REP前缀,以便在内存区域上运行。例如,REP STOSB可用于实现memset,而REP MOVSB可用于memcpy,依此类推。多年来,各种英特尔处理器在内部实现这些指令的方式大不相同。因此,Linux内核将确定它运行的CPU是否具有“良好”实现(同样基于CPUID)。如果它是好的,它将使用REP ...指令。如果没有,则使用备用例程。

  • 使用paravirtualization支持编译的内核也可以为特权指令执行此操作。在裸机上运行的内核能够执行LGDT指令,而在Xen下运行的内核必须向管理程序提供hypercall才能完成相同的操作。

  • 所有这些功能都通过flags :中的/proc/cpuinfo行公开。这些日子很长。