asm("nop");是如何工作的?

24

我从http://en.wikipedia.org/wiki/NOP读到了NOP的定义,但我仍需要一个更简单的定义。

我偶然发现了一段代码,但不确定它的作用:

switch (Something)
    {

        case this_one:
            asm ("nop");
            break;
        case other_one:
            asm ("nop");
            break;
        default:
            asm ("nop");
            break;
    }

12
asm 插入内联汇编指令, nop 是一条不做任何操作的指令。这个代码示例没有实际作用。你在哪部分遇到了困难? - Paul R
1
你的代码在技术上“什么也不做”,如果不将它与其他代码片段放入上下文中,很难说出它存在的原因。然而,它可能被用于优化目的——例如保留内存对齐。一些处理器在将内存“对齐”为2的幂块大小时表现更佳。 - Phill
8
在形而上学上,"nothing"这样的东西并不存在。NOP或No Operation意味着CPU最终会浪费时钟周期。它必须至少解码操作并注意到需要获取新指令。在这种情况下,“Nothing”意味着“程序计数器”寄存器被更改;可能不会发生其他任何副作用。 - Aki Suihkonen
1
@AlexandruCimpanu 有几个目的,下面的答案中提到了一些。还有一个没有提到的是在连续指令之间插入延迟> epsilon(如果代码与硬件一起使用可能需要)。 - SomeWittyUsername
5
个人经常使用汇编NOP作为调试代码,当我想在代码中放置断点时。 - Lundin
显示剩余5条评论
7个回答

33

nop是一种汇编指令,它什么也不做--至少是尽可能地不做任何操作,但依然可以执行机器指令,这意味着(可能)会经过极短暂的时间,这在某些实时应用中具有很有限的价值。

在这种情况下,语句asm("nop");对程序没有语义上的影响。我想唯一可能存在的原因是“强制”编译器不要折叠代码路径,如果查看目标代码或反汇编机器代码或在调试器中查看它,则可以使开关语句的机器结构可见。


2
@AlexandruCimpanu:编译器(很可能)不会优化掉asm()指令,因为它不会查看其参数。简单的break语句可以被优化掉。 - PMF
@PMFпјҡжҲ‘и®ӨдёәдҪ дёҚйңҖиҰҒдҪҝз”ЁnopгҖӮдёҖдёӘз©әзҡ„volatile asm("",,,"memory")иҜӯеҸҘе°ұеҸҜд»Ҙи§ЈеҶій—®йўҳгҖӮ - Kerrek SB
2
@PMF:nop指令会产生一条指令,而空的asm语句则不会产生任何指令... - Kerrek SB
@Alexandru Cimpanu,因为break本身将被忽略,不会生成任何代码或跳转表中的条目。 - 0___________
但在这种情况下,可能会将所有3种情况聚合为一种。为了防止这种情况发生,应该使用“asm volatile”。 - 0___________
显示剩余3条评论

15

由于没有人提到过,nop 在临界区内也可以用来“屈服”,即允许其他中断发生以减少临界区引起的中断延迟。

这通常在没有操作系统的嵌入式应用程序中非常有用,因为您经常需要在临界区间轮询变量和/或状态寄存器。

在许多RISC处理器(如ARM、AVR)上,在解除中断屏蔽的指令之后紧接着的指令仍将被屏蔽,因此如果您只是将 sei/cli 放在一起,将不会允许任何中断发生。

seicli 之间添加每个 nop,都允许一个以上的中断在临界区恢复之前发生。


8

NOP 在调试时非常有用。即使什么也不做,因为它意味着“无操作”,NOP 也是在调试器中设置断点的一条指令。我相信程序员是想以这种方式学习“Something”的值。看起来很愚蠢,但有一个经验丰富的程序员花了几个小时才找出原因。

if(Something);
{
 ...
}

我怀疑有人在使用switch()时遇到了类似的问题,因为它总是进入作用域而不依赖于Something的值。如果程序员省略了NOP,则编译器可能更容易地移除整个switch()语句。我也使用__asm__("#start");来为IOS/iPad/iPhone的最大速度中的一些代码创建明确的边界,但__asm__("nop");也可以实现相同的效果。

正如已经提到的,它可用于实时应用程序。例如,http://www.rickard.gunee.com/projects/video/pic/tetris.php 可能会使用这种方法。


嗯...有经验的程序员会保持所有编译器警告开启。此外,有经验的C程序员会形成一种下意识的反应,能够立即发现那些迷路的分号。 - Lundin
3
这个案例是真实的,但发生在很多年前。也许GCC当时没有发出警告,我不记得了。此外,人们有时候只是感到疲倦等等。 - Tõnu Samuel
1
谢谢您发布这个!我发现这个答案非常有帮助,因为我以前从未想过那样使用NOP。 - Kelsius

6

如上所述,它在嵌入式领域中有用途。有时需要等待一两个周期,才能将引脚实际设置为特定状态,尤其是在某些传感器等情况下... 在6581 CPU的旧时代,nop被用来生成延迟,因为时钟大约为0.7 MHz... 关于中断的说法也是正确的。


5
在英特尔x86上,nop是一个助记符,表示xchg eax,eax。因此,它将eax寄存器与自身交换。 还有其他形式也可以实现相同的功能,但需要更多的空间(例如,lea esi,[esi + eiz] - 将0添加到esi),但这些不是“官方”的nop指令。

4

NOP是一条汇编指令,意思是“无操作”,就像它所说的那样,它什么也不做,但它会像任何其他指令一样被CPU处理。 这意味着它将从内存中读取,将增加指令指针,但在执行阶段的指令解码之后,将不会做任何其他事情。 它经常被黑客在反向工程中使用,但我不知道它还有什么其他用途。 我认为在C编程中没有必要使用NOP指令。


如果您正在运行一个紧密的轮询循环并且不希望编译器优化掉一个空循环,那么这可能会很有用。 - BretD
@BretD 很不幸,这只适用于非常简单的uC。流水线处理器只会跳过nops,并不能保证任何延迟。例如ARM,它们对于填充很有用。 - 0___________

1
一个 nop 是一条电脑指令,它什么也不做。在这种情况下,它可能是为了防止开关被优化掉。最多只能说这是编译器的具体实现,因为没有任何东西可以阻止它解析和优化 asm 语句。
为什么要这样做?可能是为了测试机器码生成。从功能上讲,它没有用处。

3
实际上,我认为我从未见过一个编译器敢优化掉NOP指令,因为它总是有其存在的理由。例如,在快速微控制器上运行与数字逻辑电子相互作用的程序可能会使用NOP作为一种微小的延迟,以便允许逻辑电平稳定。 - Lundin
1
nop指令的成本很高,但在实时函数中可能会有用。 - Micromega
1
也许吧。这取决于具体的实现。如果在例如x86上,nop指令甚至没有被放入PIQ中,那也不会让我感到惊讶。但是,它确实占用了内存,我想这确实会产生一些影响。 - Per Johansson
1
对于大多数编译器而言,这并不是真的。默认情况下,asm语句被视为易变的(无法优化),除非您添加约束条件以让编译器了解内联代码的目的(使用的寄存器,内存修改等)。 - kuroi neko
1
@Lundin,现代uP/uC上不使用nop指令来进行延迟,它只用于填充。如果您需要精确的延迟,只能使用屏障指令。详情请参考:http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0553a/CHDJJGFB.html。 - 0___________
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接