无尽的While循环会占用CPU资源吗?

28

据我所了解,您编写的 Linux 守护进程会在一个无限循环中监听请求。
类似于...

int main() {
    while(1) {
        //do something...
    }
}

参考:http://www.thegeekstuff.com/2012/02/c-daemon-process/

据我所知,将程序休眠可以使其进入等待模式,以防止它占用资源。

1.如果我想让我的守护进程每隔1秒钟检查一次请求,以下方法是否会消耗资源?

int main() {
    while(1) {
        if (request) {
            //do something...
        }
        sleep(1)
    }
}

2.如果我去掉睡眠,那么CPU消耗会增加100%吗?

3.是否可能运行一个无限循环而不会占用资源?比如...如果它只是循环自己。或者只是sleep(1)。

无限循环和CPU资源对我来说是个谜。


1
睡眠告诉调度程序在特定的时间段内(为了保持简单)让其他进程在CPU上运行,而不是您的进程。 在睡眠期间,您的程序不会消耗任何CPU资源。 即使只睡眠几毫秒,在像您展示的无限循环中也可以使事情变得顺畅。 - Niklas R
3
如果你在监听,那么你应该使用一些多路复用的系统调用,例如poll(2)(或者旧的、几乎废弃的select(2))。 - Basile Starynkevitch
3个回答

17

有没有可能在不消耗资源的情况下运行一个无限循环?比如说..它除了循环自己以外什么都不做。或者只是 sleep(1)。

有一个更好的选择。
你可以使用 信号量(semaphore),在循环开始时保持阻塞状态,只有当你想让循环执行时才发出信号。
请注意,这不会消耗任何资源。


1
“不会占用任何资源”并不完全准确...创建信号量会使用内核资源和对象,这是一个相当重的操作。等待信号量是一种内核模式操作,也将导致上下文切换到内核模式。 - Mahmoud Al-Qudsi
如果使用超时,信号量可能是合适的选择,但仍然最好使用sleep。如果您希望代码在超时或某些外部事件上触发,则信号量最相关。 - Kevin A. Naudé
谢谢提到信号量。等有时间了我会去探索一下 :) - resting

17
pollselect调用(Basile Starynkevitch在评论中提到)或信号量(Als在回答中提到)是等待请求的正确方法,这取决于情况。如果没有pollselect操作系统,则应有类似的替代品。 sleepYieldProcessorsched_yield都不适合做这件事,原因如下。 YieldProcessorsched_yield仅将进程移动到可运行队列的末尾,但仍然保持可运行状态。其效果是允许同一优先级或更高优先级的其他进程执行,但当这些进程完成(或者没有进程时),调用YieldProcessorsched_yield的进程继续运行。这会导致两个问题。一是较低优先级的进程仍不能运行。另一个问题是,这会使处理器始终运行,使用能量。我们希望操作系统识别何时不需要运行任何进程,并将处理器置于低功耗状态。 sleep可能允许这种低功耗状态,但它猜测下一个请求到来所需的时间,当没有必要时会重复唤醒处理器,而且会使进程对请求的响应变得不那么灵敏,因为进程将继续睡眠,直到到达请求的时间截止日期,即使有请求需要被服务。 pollselect调用正是为这种情况设计的。它们告诉操作系统该进程要在其IO通道之一上服务一个请求,但否则没有其他工作可做。这使操作系统能够将该进程标记为不可运行,并在合适时将处理器置于低功耗状态。使用信号量提供相同的行为,但唤醒进程的信号来自另一个进程引发的信号量而不是在I/O通道中产生的活动。当以这种方式到达执行某些工作的信号时,信号量是合适的;只需使用更适合您情况的poll或信号量。
对于pollselect或信号量导致内核模式调用的批评是无关紧要的,因为其他方法也会导致内核模式调用。进程不能自己睡眠;它必须调用操作系统来请求。同样,YieldProcessorsched_yield也向操作系统发出请求。

3
简短的回答是:是的——取消睡眠会导致100%的CPU使用率——但答案取决于一些额外的细节。它会消耗所有可以获得的CPU,除非...
1. 循环体很简单,并被优化掉了。 2. 循环包含阻塞操作(如文件或网络操作)。您提供的链接建议避免这种情况,但通常最好阻塞,直到发生相关事件。
编辑:对于您的情况,我支持@Als提出的建议。
编辑2:我预计这个答案之所以会受到-1的评价,是因为我声称阻塞操作实际上是一个好主意。(如果你给出-1,你应该在评论中留下动机,以便我们都能学到东西。)
当前流行的思想是非阻塞(基于事件)IO很好,而阻塞则很差。这种观点过于简单化,因为它假定所有执行IO的软件都可以通过使用非阻塞操作来提高吞吐量。
什么?我真的在暗示使用非阻塞IO实际上可能会降低吞吐量吗?是的。当一个进程只服务于单个活动时,实际上最好使用阻塞IO,因为阻塞IO只会消耗已经在进程存在中支付的资源。
相反,非阻塞IO可能会带来比简单阻塞IO更大的固定开销。如果进程无法提供可以交错的其他IO,则使用非阻塞设置没有任何收益。(实际上,不适当使用非阻塞IO的最大成本仅在于增加了代码复杂性。除此之外,这个话题基本上只是一个思考练习。)
在阻塞IO下,我们依靠操作系统来安排那些可以取得进展的进程。这就是操作系统的设计目的。
在非阻塞IO下,我们有更高的设置成本,但可以在交错工作之间共享进程及其线程的资源。因此,非阻塞IO非常适合任何服务于多个独立活动的进程,例如Web服务器。获得的吞吐量远远超过非阻塞IO的固定成本开销。

软件可能无法通过使用非阻塞I/O来提高吞吐量,但它总是会改善响应能力。 是的,您应该阻塞,但它应该是一个等待各种事件的阻塞调用,而不是一个阻塞的I/O调用,导致用户输入被忽略,直到I/O完成。selectpollaio_suspend都是例子。你说,“如果进程无法提供可以交错的附加IO”,但总有至少一个额外的输入可以发生……用户请求退出。 - Ben Voigt
@BenVoigt 这是真的。交互式应用程序不应该无限期地阻塞,例如,信号量应该使用超时。话虽如此,posix后端服务即使执行阻塞操作也可以接收信号。总之,你提出了一个值得记住的好观点 - 谢谢。 - Kevin A. Naudé

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