Linux select()vs ppoll()vs pselect() [英] Linux select() vs ppoll() vs pselect()
问题描述
在我的应用程序中,有一个专用于
In my application, there is a io-thread, that is dedicated for
- 以自定义协议包装从应用程序收到的数据
- 通过tcp/ip发送数据+自定义协议数据包
- 通过tcp/ip接收数据+自定义协议数据包
- 解包自定义协议并将数据传递给应用程序.
应用程序通过不同的线程处理数据.此外,要求还规定未确认的窗口大小应为1,即在任何时候都应该只有一个待处理的未确认消息.这意味着,如果io-thread在套接字上调度了一条消息,它将不会再发送任何消息,直到它听到来自接收方的确认为止. 应用程序的处理线程通过管道与io线程通信.如果有人从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
- 在套接字和管道描述符上使用PPoll()
- 使用Select()
- 使用PSelect()
我有以下问题
-
select()和poll()之间的决定.我的应用程序仅处理少于50个文件描述符.可以假设我选择select还是poll都可以吗?
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 ?
-
select()和pselect()之间的决定.我阅读了Linux文档,并说明了信号和select()之间的竞争状况.我没有信号的经验,所以有人可以更清楚地说明比赛条件和select()吗?
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 *
自变量(vs select()
和poll()
)对每个"p变量"具有相同的影响.如果您不使用信号,那么您将无所适从,因此基本问题实际上是关于效率和易于编程的问题.
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答案:什么是轮询和选择之间的区别.
Meanwhile there's already a stackoverflow.com answer here: what are the differences between poll and select.
对于种族:一旦开始使用信号(无论出于何种原因),您就会了解到,通常,信号处理程序应该只设置类型为volatile sig_atomic_t
的变量以表明已检测到信号.这样做的根本原因是,许多库调用不是重新进入,这样,您就可以在常规"过程中传递信号.例如,仅将消息打印到流式数据结构(例如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
的类似代码(另请参见
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 ...
}
这很好,直到您开始需要进行等待某些输入/输出的调用,例如select
或poll
. 等待"操作需要等待该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(...) */
}
这会缩小竞赛窗口",因为现在您可以在执行一些工作"代码时检测到中断,如果发生了,就可以了;但还是有一场比赛,因为中断可以在您测试变量后之后发生,而在 select-or-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屋!