【问题标题】:CPU idle loop without Sleep没有睡眠的 CPU 空闲循环
【发布时间】:2025-12-23 22:20:19
【问题描述】:

我现在正在学习 WIN32 ASM,我想知道是否有类似“空闲”无限循环的东西,它根本不消耗任何资源。基本上我需要一个正在运行的进程来进行实验,如下所示:

loop:
  ; alternative to sleep...
  jmp loop

有什么东西可以让进程闲置吗?

【问题讨论】:

  • 代表怎么样?不;? sleep()sched_yield() 等是要求操作系统执行其他操作的系统调用。您可以在汇编程序中进行这些调用。但这些并不是真正让 CPU 什么都不做的指令,而是更高级别的东西。
  • TerminateProcess() 是停止执行指令的好方法。编写无限循环不是,也没有明显的用处。 HLT 指令只能由特权代码使用。
  • 讨厌问一个明显的问题,但是......不睡觉完全按照您的 cmets 对答案的要求做?您希望核心做其他事情或完全空闲一段时间,然后您想要控制权。这就是Sleep 所做的。你为什么要寻找替代品?有什么问题?
  • 这对汇编语言不好用。当你认为你可以比编译器做得更好时,你会使用汇编语言,通常是为了让事情变得更快。当您的目标是什么都不做时,就不需要速度。
  • @BenjaminWeiss:那么答案是否定的。您希望操作系统安排另一个线程在 CPU 上运行(如果一个线程已准备好运行),或者如果操作系统无法让该内核执行其他工作,则让内核暂时休眠.这显然需要调用操作系统,因为只有操作系统调度程序知道该内核在任何特定时间可能执行的其他工作,并且只有操作系统可以安排中断以将内核从睡眠中唤醒。这正是Sleep 所做的具体而准确的事情。

标签: windows assembly


【解决方案1】:

使用 ring 0 访问级别(如内核驱动程序),您可以使用 x86 HLT 操作码,但您需要具备系统编程技能才能真正理解如何使用它。以这种方式使用 HLT 需要启用中断(未屏蔽)并保证发生中断(例如系统定时器),因为从中断返回将执行 HLT 之后的下一条指令。

如果没有 ring 0 访问权限,您将永远找不到任何 x86 操作码进入“空闲”模式... 您只能找到一些耗电较少的指令(无内存访问、无缓存访问、无 FPU 访问、低 ALU 使用率......)。

【讨论】:

    【解决方案2】:

    注意:您不应该使用空循环使应用程序空闲。它会将您的处理器加载到 100%

    当 Win32 环境中没有 GUI 活动时,有几种方法可以使应用程序空闲。主要(记录在 MSDN 中)是在主循环中使用“GetMessage”函数,以便从消息队列中提取消息。 当消息队列为空时,该函数将处于空闲状态,消耗非常少的处理器时间,等待消息到达消息队列。

    下面是一个例子,使用 FASM 宏库:

    msg_loop:
          invoke  GetMessage, msg, NULL, 0, 0
          cmp     eax, 1
          jb      end_loop
          jne     msg_loop
          invoke  TranslateMessage, msg
          invoke  DispatchMessage, msg
          jmp     msg_loop
    

    当您想要捕捉应用程序进入空闲状态的时刻并进行一些低优先级的一次性处理(例如,根据应用程序的状态启用/禁用工具栏上的按钮时)使用另一种方法)。

    在这种情况下,必须使用 PeekMessage 和 WaitMessage 的组合。 PeekMessage 函数会立即返回,即使消息队列为空。这样,您可以检测到这种情况并提供一些空闲任务来完成,然后您必须调用 WaitMessage 以使等待传入消息的进程空闲。

    这是我的代码中的一个简化示例(使用 FreshLib 宏):

    ; Main message loop
    Run:
            invoke  PeekMessageA, msg, 0, 0, 0, PM_REMOVE
            test    eax,eax
            jz      .empty
    
            cmp     [msg.message], WM_QUIT
            je      .terminate
    
            invoke  TranslateMessage, msg
            invoke  DispatchMessageA, msg
            jmp     Run
    
    .empty:
            call    OnIdle
            invoke  WaitMessage
            jmp     Run
    
    .terminate:
            FinalizeAll
            stdcall TerminateAll, 0
    

    【讨论】:

      【解决方案3】:

      是的,有些架构支持固有空闲状态 AKA halt

      例如,在 x86 中 hlt,操作码 0xf4。可能只能在特权模式下调用。

      CPU Switches from User mode to Kernel Mode : What exactly does it do? How does it makes this transition?

      How to completely suspend the processor?

      我找到了一个 Linux 的用户空间示例 here:

      .section .rodata
      greeting:
      .string "Hello World\n"
      .text
      _start:
      mov $12,%edx /* write(1, "Hello World\n", 12) */
      mov $greeting,%ecx
      mov $1,%ebx
      mov $4,%eax /* write is syscall 4 */
      int $0x80
      xorl %ebx, %ebx /* Set exit status and exit */
      mov $0xfc,%eax
      int $0x80
      hlt /* Just in case... */
      

      【讨论】:

      • 操作系统会允许吗? IE。需要什么保护级别?
      • 这需要中断或类似的东西来唤醒它。它对用户进程没有用。
      • hlt 可以从处理器的内核模式读取。所以你需要一个系统调用。
      【解决方案4】:

      “消耗资源”是什么意思?

      如果你只想要一个什么都不做的命令?如果是这样,nop 会这样做,您甚至可以随意循环:rep; nop。但是,CPU实际上会忙于工作:执行“无操作”指令。

      如果你想要一条会导致 CPU 本身停止的指令,那么你有点不走运:虽然有办法做到这一点,但你不能从用户空间做到这一点。

      【讨论】:

      • 消耗资源我其实是指最低成本的CPU百分比。
      • @BenjaminWeiss:所以你希望核心能够做其他事情?然后你必须调用操作系统来告诉它在那个 CPU 上调度另一个线程。致电SwitchToThread。请注意,使用率仍将至少为 25%——如果核心可以做其他事情,它就会被使用。如果核心不能做任何其他事情,它会尽可能快地运行你的代码,从而被使用。
      • 这需要调用我不想调用的操作系统。就没有其他的睡眠吗?
      • 为什么不想调用操作系统?无论如何,您的应用程序已经是特定于系统的 - 没有适用于任何东西的神奇“通用二进制”。除非您打算在裸机上运行代码(例如 BIOS ROM 扩展等)
      • @BenjaminWeiss:除了运行代码之外,您还希望核心执行其他操作,对吗?这意味着操作系统必须安排另一个线程来使用该内核。如果不调用操作系统,这显然是不可能的。
      【解决方案5】:

      你不能同时拥有它。您可以消耗 CPU,也可以让它做其他事情。正如 Vlad Lazarenko 建议的那样,您可以使用rep; nop;(也称为pause)来节省电力并避免剥夺其他核心的资源。但是,如果您循环而不将核心让给另一个进程,那么至少该虚拟核心不能做任何其他事情。

      【讨论】:

      • 我试过了,但它仍然占用了我整个 CPU 的 25%。 (25% 因为我有 QuadCore?!)
      • @BenjaminWeiss:除了运行代码之外,您是否希望核心执行其他操作?或不?没有第三种选择。如果该核心正在运行您的代码,它就不能做任何其他事情。因此使用了你的四个核心之一,25%。
      • 我希望进程消耗几乎 0% 的 CPU。 WINAPI 调用睡眠暂停进程(甚至可能暂停当前正在运行的线程)。我想要一个替代方案。
      • @BenjaminWeiss:我不关注。睡眠准确地做你想要的。另一种选择会更差,或者充其量不会更糟。为什么你想要一个替代品?