更改模型编程。这个怎么运作?
Changing a model programming. How it works?
请解释一下。如果我知道正确的话,每个 CPU 都有不同的编程模型。假设我有 CPU 可以执行 XYZ 汇编指令。然后我将编译程序并将其发送给我的朋友,其中有 CPU 没有 XYZ 汇编指令。尽管如此,我认为程序会起作用。一次编译的程序一般是如何独立于硬件架构的?
当然,二进制文件只能在其编译体系结构的机器上执行。例如,您不能 运行 x86 上的 ARM 可执行文件。大多数时候,程序加载器甚至不会让您尝试。
我们能够成功分发软件的二进制版本(针对特定架构,如 x86-64),因为处理器必须支持一组基本指令。您的 Intel CPU 和您伙伴的 AMD CPU 仍然执行相同的基本指令集。默认情况下,GCC 只会发出属于此类别的指令。
此外,可以允许 GCC 发出更专门的指令,这些指令属于特定指令集 扩展。例如,SSE 扩展有各种迭代,执行矢量数学。因为不是所有的 CPU 都实现了这些指令,所以你必须明确地告诉 GCC 它被允许使用它们。您可以使用 GCC 的 -march
和 -mtune
标志来执行此操作。 -march=native
将编译一个程序以在您的机器上运行良好,但如果您尝试 运行 在具有不同 CPU.
的另一台机器上使用相同的二进制文件,则可能无法正常工作
虽然如果要编译您的代码,该策略很有效,但我们可以实施一种混合方法。这涉及使用 CPUID
指令来确定(在 运行 时间)当前 CPU 上可用的感兴趣的特征。然后,基于该信息,我们可以调用使用不同指令集扩展的不同函数。这些通常会在汇编中手动编码,或者在 C 中使用 built-ins 用于相关架构。
Linux 内核实际上也在 运行 时执行此操作,尽管通常以更令人印象深刻的方式进行。 x86 上的几个示例:
在SMP系统中,某些指令需要x86 LOCK
前缀,以防止多个CPU同时修改同一个变量。然而,在单处理器 (UP) 系统上,这条指令是不必要的,并且会减慢速度。如果为 SMP 编译的内核确定它在 UP 系统上 运行ning,它实际上会 NOP
-out LOCK
指令,使其无用。也就是说,它在顶部写了一条 "no-op" 指令。
字符串指令可以被赋予 REP
前缀以在内存区域上运行。例如,REP STOSB
可以用来实现memset
,而REP MOVSB
可以用来实现memcpy
,等等。多年来,各种英特尔处理器在内部实现这些指令的方式大不相同。因此,Linux 内核将确定 运行 所在的 CPU 是否具有 "good" 实现(再次基于 CPUID
)。如果好的话,它会使用 REP ...
指令。如果不是,则使用备用例程。
使用 paravirtualization support also do this for privileged instructions. A kernel running on bare metal is able to execute the LGDT
instruction, while a kernel running under Xen must make a hypercall 编译的内核到管理程序以完成相同的任务。
所有这些功能都通过 /proc/cpuinfo
中的 flags :
行公开。这些天名单很长。
请解释一下。如果我知道正确的话,每个 CPU 都有不同的编程模型。假设我有 CPU 可以执行 XYZ 汇编指令。然后我将编译程序并将其发送给我的朋友,其中有 CPU 没有 XYZ 汇编指令。尽管如此,我认为程序会起作用。一次编译的程序一般是如何独立于硬件架构的?
当然,二进制文件只能在其编译体系结构的机器上执行。例如,您不能 运行 x86 上的 ARM 可执行文件。大多数时候,程序加载器甚至不会让您尝试。
我们能够成功分发软件的二进制版本(针对特定架构,如 x86-64),因为处理器必须支持一组基本指令。您的 Intel CPU 和您伙伴的 AMD CPU 仍然执行相同的基本指令集。默认情况下,GCC 只会发出属于此类别的指令。
此外,可以允许 GCC 发出更专门的指令,这些指令属于特定指令集 扩展。例如,SSE 扩展有各种迭代,执行矢量数学。因为不是所有的 CPU 都实现了这些指令,所以你必须明确地告诉 GCC 它被允许使用它们。您可以使用 GCC 的 -march
和 -mtune
标志来执行此操作。 -march=native
将编译一个程序以在您的机器上运行良好,但如果您尝试 运行 在具有不同 CPU.
虽然如果要编译您的代码,该策略很有效,但我们可以实施一种混合方法。这涉及使用 CPUID
指令来确定(在 运行 时间)当前 CPU 上可用的感兴趣的特征。然后,基于该信息,我们可以调用使用不同指令集扩展的不同函数。这些通常会在汇编中手动编码,或者在 C 中使用 built-ins 用于相关架构。
Linux 内核实际上也在 运行 时执行此操作,尽管通常以更令人印象深刻的方式进行。 x86 上的几个示例:
在SMP系统中,某些指令需要x86
LOCK
前缀,以防止多个CPU同时修改同一个变量。然而,在单处理器 (UP) 系统上,这条指令是不必要的,并且会减慢速度。如果为 SMP 编译的内核确定它在 UP 系统上 运行ning,它实际上会NOP
-outLOCK
指令,使其无用。也就是说,它在顶部写了一条 "no-op" 指令。字符串指令可以被赋予
REP
前缀以在内存区域上运行。例如,REP STOSB
可以用来实现memset
,而REP MOVSB
可以用来实现memcpy
,等等。多年来,各种英特尔处理器在内部实现这些指令的方式大不相同。因此,Linux 内核将确定 运行 所在的 CPU 是否具有 "good" 实现(再次基于CPUID
)。如果好的话,它会使用REP ...
指令。如果不是,则使用备用例程。使用 paravirtualization support also do this for privileged instructions. A kernel running on bare metal is able to execute the
LGDT
instruction, while a kernel running under Xen must make a hypercall 编译的内核到管理程序以完成相同的任务。所有这些功能都通过
/proc/cpuinfo
中的flags :
行公开。这些天名单很长。