Linux select() vs ppoll() vs pselect() [英] Linux select() vs ppoll() vs pselect()

查看:13
本文介绍了Linux select() vs ppoll() vs pselect()的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的应用程序中,有一个 io-thread,专用于

In my application, there is a io-thread, that is dedicated for

  1. 将从应用程序接收到的数据封装在自定义协议中
  2. 通过 tcp/ip 发送数据+自定义协议包
  3. 通过tcp/ip接收数据+自定义协议包
  4. 解开自定义协议并将数据交给应用程序.

应用程序通过不同的线程处理数据.此外,要求规定未确认窗口大小应为 1,即在任何时候都应该只有一条未确认的未确认消息.这意味着如果 io-thread 已经通过套接字发送了一条消息,它不会再发送任何消息,直到它从接收者那里听到一个 ack.应用程序的处理线程通过管道与 io-thread 通信.如果有人从 linux CLI 键入 ctrl+C,应用程序需要正常关闭.因此,鉴于这些要求,我有以下选择

Application processes the data over a different thread. Additionally, the requirements dictate that the unacknowledged window size should be 1, i.e. there should be only one pending unacknowledged message at anytime. This implies that if io-thread has dispatched a message over the socket, it will not send any more messages, till it hears an ack from the receiver. Application's processing thread communicates to io-thread via pipe. Application needs to shut gracefully if someone from linux CLI types ctrl+C. Thus, given these requirements, i have following options

  1. 在套接字和管道描述符上使用 PPoll()
  2. 使用 Select()
  3. 使用 PSelect()

我有以下问题

  1. select() 和 poll() 之间的决定.我的应用程序只处理少于 50 个文件描述符.可以假设我选择 select 还是 poll 没有区别吗?

  1. The decision between select() and poll(). My application only deals with less than 50 file descriptors. Is it okay to assume there would be no difference whether i choose select or poll ?

  1. select() 和 pselect() 之间的决定.我阅读了 linux 文档,它说明了信号和 select() 之间的竞争条件.我没有信号经验,所以有人可以更清楚地解释比赛条件和 select() 吗?是否与有人在 CLI 上按 ctrl+C 并且应用程序未停止有关?

  1. Decision between select() and pselect(). I read the linux documentation and it states about race condition between signals and select(). I dont have experience with signals, so can someone explain more clearly about the race condition and select() ? Does it have something to do with someone pressing ctrl+C on CLI and application not stopping?

pselect 和 ppoll() 之间的决定?关于一个与另一个的任何想法

Decision between pselect and ppoll() ? Any thoughts on one vs the other

推荐答案

我建议先比较 select()poll().Linux 也同时提供 pselect()ppoll();以及 pselect()ppoll() 的额外 const sigset_t * 参数(对比 select()poll()) 对每个p-variant"具有相同的效果.如果您不使用信号,则无需防范竞争,因此基本问题实际上是关于效率和编程的易用性.

I'd suggest by starting the comparison with select() vs poll(). Linux also provides both pselect() and ppoll(); and the extra const sigset_t * argument to pselect() and ppoll() (vs select() and poll()) has the same effect on each "p-variant", as it were. If you are not using signals, you have no race to protect against, so the base question is really about efficiency and ease of programming.

同时这里已经有 stackoverflow.com 的答案:什么是poll 和 select 的区别.

Meanwhile there's already a stackoverflow.com answer here: what are the differences between poll and select.

至于比赛:一旦你开始使用信号(无论出于何种原因),你就会了解到,一般来说,信号处理程序应该只设置一个 volatile sig_atomic_t 类型的变量来指示信号已被检测到.根本原因是许多库调用不是re-entrant,并且当您处于"这样的例程中时,可以传递信号.例如,简单地将消息打印到流式数据结构,例如 stdout (C) 或 cout (C++) 可能会导致重入问题.

As for the race: once you start using signals (for whatever reason), you will learn that in general, a signal handler should just set a variable of type volatile sig_atomic_t to indicate that the signal has been detected. The fundamental reason for this is that many library calls are not re-entrant, and a signal can be delivered while you're "in the middle of" such a routine. For instance, simply printing a message to a stream-style data structure such as stdout (C) or cout (C++) can lead to re-entrancy issues.

假设您有使用 volatile sig_atomic_t flag 变量的代码,可能是为了捕获 SIGINT,类似这样(另请参阅 http://pubs.opengroup.org/onlinepubs/007904975/functions/sigaction.html):

Suppose you have code that uses a volatile sig_atomic_t flag variable, perhaps to catch SIGINT, something like this (see also http://pubs.opengroup.org/onlinepubs/007904975/functions/sigaction.html):

volatile sig_atomic_t got_interrupted = 0;
void caught_signal(int unused) {
    got_interrupted = 1;
}
...
    struct sigaction sa;
    sa.sa_handler = caught_signal;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;
    if (sigaction(SIGINT, &sa, NULL) == -1) ... handle error ...
    ...

现在,在您的代码主体中,您可能希望运行直到被中断":

Now, in the main body of your code, you might want to "run until interrupted":

    while (!got_interrupted) {
         ... do some work ...
    }

这很好,直到您开始需要进行等待某些输入/输出的调用,例如 selectpoll.等待"动作需要等待该 I/O——但它需要等待 SIGINT 中断.如果你只是写:

This is fine up until you start needing to make calls that wait for some input/output, such as select or poll. The "wait" action needs to wait for that I/O—but it also needs to wait for a SIGINT interrupt. If you just write:

    while (!got_interrupted) {
        ... do some work ...
        result = select(...); /* or result = poll(...) */
    }

那么中断可能发生在就在您调用select()poll() 之前,而不是之后.在这种情况下,你确实被打断了——变量 got_interrupted 被设置——但在那之后,你开始等待.您应该在开始等待之前检查 got_interrupted 变量,而不是之后.

then it's possible that the interrupt will happen just before you call select() or poll(), rather than afterward. In this case, you did get interrupted—and the variable got_interrupted gets set—but after that, you start waiting. You should have checked the got_interrupted variable before you started waiting, not after.

你可以试试写:

    while (!got_interrupted) {
        ... do some work ...
        if (!got_interrupted)
            result = select(...); /* or result = poll(...) */
    }

这会缩小竞赛窗口",因为现在您将检测到中断是否在您执行做一些工作"代码时发生;但是仍然存在竞争,因为中断可能发生在就在您测试变量之后,但就在选择或轮询之前.

This shrinks the "race window", because now you'll detect the interrupt if it happens while you're in the "do some work" code; but there is still a race, because the interrupt can happen right after you test the variable, but right before the select-or-poll.

解决方案是使用 sigprocmask 的信号阻塞属性(或者,在 POSIX 线程代码中,pthread_sigmask):

The solution is to make the "test, then wait" sequence "atomic", using the signal-blocking properties of sigprocmask (or, in POSIX threaded code, pthread_sigmask):

sigset_t mask, omask;
...
while (!got_interrupted) {
    ... do some work ...
    /* begin critical section, test got_interrupted atomically */
    sigemptyset(&mask);
    sigaddset(&mask, SIGINT);
    if (sigprocmask(SIG_BLOCK, &mask, &omask))
        ... handle error ...
    if (got_interrupted) {
        sigprocmask(SIG_SETMASK, &omask, NULL); /* restore old signal mask */
        break;
    }
    result = pselect(..., &omask); /* or ppoll() etc */
    sigprocmask(SIG_SETMASK, &omask, NULL);
    /* end critical section */
}

(上面的代码实际上并没有那么好,它的结构是为了说明而不是效率——稍微不同地执行信号掩码操作,并以不同的方式放置被中断"测试会更有效).

(the above code is actually not that great, it's structured for illustration rather than efficiency -- it's more efficient to do the signal mask manipulation slightly differently, and place the "got interrupted" tests differently).

在您真正开始需要捕获 SIGINT 之前,您只需要比较 select()poll()(如果您开始需要大量描述符,一些基于事件的东西,如 epoll() 比任何一个都更有效).

Until you actually start needing to catch SIGINT, though, you need only compare select() and poll() (and if you start needing large numbers of descriptors, some of the event-based stuff like epoll() is more efficient than either one).

这篇关于Linux select() vs ppoll() vs pselect()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆