在我的应用程序中,有一个io线程,专用于
应用程序通过不同的线程处理数据。此外,要求还规定未确认的窗口大小应为1,即随时都应只有一条待处理的未确认消息。这意味着,如果io- thread在套接字上调度了一条消息,它将不会再发送任何消息,直到它听到来自接收方的确认为止。应用程序的处理线程通过管道与io线程通信。如果有人从Linux CLI输入ctrl + C,则应用程序需要正常关闭。因此,鉴于这些要求,我有以下选择
我有以下问题
select()和poll()之间的决定。我的应用程序仅处理少于50个文件描述符。可以假设我选择select或poll都没有区别吗?
在select()和pselect()之间进行决策。我阅读了Linux文档,并说明了信号和select()之间的竞争状态。我没有信号的经验,所以有人可以更清楚地说明比赛条件和select()吗?这与有人在CLI上按ctrl + C且应用程序没有停止有关吗?
pselect和ppoll()之间的决定?对一个人的任何想法
我建议通过与select()vs 开始比较poll()。Linux还提供pselect()和ppoll();和和(vs 和)的额外const sigset_t *参数对每个“ p变量”具有相同的作用。如果您不使用信号,那么您就没有竞争的机会,因此,基本问题实际上是关于效率和易于编程的问题。pselect()``ppoll()``select()``poll()
select()
poll()
pselect()
ppoll()
const sigset_t *
pselect()``ppoll()``select()``poll()
至于比赛:一旦开始使用信号(无论出于何种原因),您将了解到,信号处理程序通常应设置一个类型变量,volatile sig_atomic_t以指示已检测到信号。造成这种情况的根本原因是,许多库调用都不是可重入的,并且可以在您处于此类例程“中间”时传递信号。例如,仅将消息打印到流样式的数据结构(例如stdout(C)或cout(C ++))会导致重新进入问题。
volatile sig_atomic_t
stdout
cout
假设您有使用volatile sig_atomic_t flag变量(可能是catch)的代码,SIGINT如下所示(另请参见http://pubs.opengroup.org/onlinepubs/007904975/functions/sigaction.html):
volatile sig_atomic_t flag
SIGINT
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 ... ...
现在,在代码主体中,您可能希望“运行直到被中断”:
while (!got_interrupted) { ... do some work ... }
这很好,直到您开始需要进行等待某些输入/输出的调用(例如select或)为止poll。“等待”操作需要等待该I / O,但 也 需要等待SIGINT中断。如果您只写:
select
poll
while (!got_interrupted) { ... do some work ... result = select(...); /* or result = poll(...) */ }
那么可能会 在 您调用select()或 之前poll()而不是之后发生中断。在这种情况下,您确实被打断了,并且变量got_interrupted被设置了,但是在那之后,您开始等待。您应该got_interrupted在开始等待之前而不是之后检查变量。
got_interrupted
您可以尝试编写:
while (!got_interrupted) { ... do some work ... if (!got_interrupted) result = select(...); /* or result = poll(...) */ }
这会缩小“竞赛窗口”,因为现在您可以在“执行一些工作”代码时检测到中断(如果发生);但是仍然存在竞争,因为中断可能 在 您测试变量 后立即 发生,而 在 选择或轮询 之前 发生。
解决方案是使用sigprocmask(或在POSIX线程代码中pthread_sigmask)的信号阻止属性使“测试,然后等待”序列“原子”化:
sigprocmask
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 */ }
(上面的代码实际上并不是那么好,它只是为了说明而不是为了效率而设计的-稍微不同地进行信号屏蔽操作,并以不同的方式放置“中断”测试会更加有效)。
但是,直到您真正开始需要捕获之前SIGINT,您只需要比较select()和poll()(并且如果您开始需要大量的描述符,则某些基于事件的东西(例如,epoll()其中之一)要比任何一个都更有效)。
epoll()