Assembly 更改模型编程。它是如何工作的?

Assembly 更改模型编程。它是如何工作的?,assembly,cpu,hardware,Assembly,Cpu,Hardware,请给我解释一下。如果我知道的没错,每个CPU都有不同的编程模式。让我们假设,我有可以执行XYZ汇编指令的CPU。然后我将编译程序并发送给我的朋友,他们的CPU没有XYZ汇编指令。尽管如此,我认为这个计划会奏效。如何使编译后的程序通常独立于硬件体系结构?当然,二进制文件只能在编译后的体系结构的机器上执行。例如,您不能在x86上运行ARM可执行文件。大多数时候,程序加载器甚至不允许您尝试 我们能够成功地分发二进制版本的软件(对于特定的体系结构,如x86-64),因为处理器必须支持一组基本指令。您的I

请给我解释一下。如果我知道的没错,每个CPU都有不同的编程模式。让我们假设,我有可以执行XYZ汇编指令的CPU。然后我将编译程序并发送给我的朋友,他们的CPU没有XYZ汇编指令。尽管如此,我认为这个计划会奏效。如何使编译后的程序通常独立于硬件体系结构?

当然,二进制文件只能在编译后的体系结构的机器上执行。例如,您不能在x86上运行ARM可执行文件。大多数时候,程序加载器甚至不允许您尝试

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

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

如果您的代码要编译,那么这种策略很有效,但我们可以实现一种混合方法。这涉及到使用
CPUID
指令来确定(在运行时)当前CPU上可用的感兴趣的功能。然后,基于这些信息,我们可以调用使用不同指令集扩展的不同函数。这些代码通常在汇编中手工编码,或者在C中使用所讨论的体系结构的

Linux内核实际上也在运行时这样做,尽管通常是以一种更令人印象深刻的方式。x86上的几个示例:

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

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

  • 使用支持编译的内核也可以对特权指令执行此操作。在裸机上运行的内核能够执行
    LGDT
    指令,而在Xen下运行的内核必须与hypervisor进行通信才能完成相同的任务

  • 所有这些功能都是通过
    /proc/cpuinfo
    中的
    标志:
    行公开的。这些天的名单很长


当然,二进制文件只能在其编译架构的机器上执行。例如,您不能在x86上运行ARM可执行文件。大多数时候,程序加载器甚至不允许您尝试

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

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

如果您的代码要编译,那么这种策略很有效,但我们可以实现一种混合方法。这涉及到使用
CPUID
指令来确定(在运行时)当前CPU上可用的感兴趣的功能。然后,基于这些信息,我们可以调用使用不同指令集扩展的不同函数。这些代码通常在汇编中手工编码,或者在C中使用所讨论的体系结构的

Linux内核实际上也在运行时这样做,尽管通常是以一种更令人印象深刻的方式。x86上的几个示例:

  • 在系统中,某些指令需要x86
    LOCK
    前缀,以防止多个CPU同时修改相同的变量。但是,在单处理器(UP)系统上,此指令是不必要的,并且会减慢速度。如果为SMP编译的内核确定它正在UP系统上运行,它实际上将
    N