Linux select()vs ppoll()vs pselect()

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

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

应用程序通过不同的线程处理数据。 此外,这些要求规定未确认的窗口大小应该是1,即,在任何时候只应该有一个未确认的未确认消息。 这意味着,如果io线程已经通过套接字发送了一个消息,它将不会再发送任何消息,直到它听到来自接收方的一个消息。 应用程序的处理线程通过pipe道与io-thread通信。 应用程序需要正常closures,如果有人从Linux CLItypesctrl + C。 因此,鉴于这些要求,我有以下选项

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

我有以下问题

  1. select()和poll()之间的决定。 我的应用程序只处理less于50个文件描述符。 是否可以假设我selectselect还是投票是没有区别的?

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

    2. pselect和ppoll()之间的决定? 任何想法在一个之间

Solutions Collecting From Web of "Linux select()vs ppoll()vs pselect()"

我建议通过开始比较select() vs poll() 。 Linux也提供了pselect()ppoll() ; 和pselect()ppoll() (vs select()poll() )的额外const sigset_t *参数在每个“p-variant”上的效果都是一样的。 如果你不使用信号,你就没有保护的竞争,所以基本的问题就是编程的效率和易用性。

同时这里已经有了一个stackoverflow.com的答案: 轮询和选择之间有什么区别 。

至于比赛:一旦你开始使用信号(无论出于何种原因),你将会知道一般情况下,一个信号处理程序应该设置一个类型为volatile sig_atomic_t的变量volatile sig_atomic_t来指示信号已经被检测到。 这样做的根本原因在于,许多图书馆电话是不可重入的 ,当你处于这样一个例程的中间时,可以传递一个信号。 例如,简单地将消息打印到流风格的数据结构(如stdout (C)或cout (C ++))可能会导致重入问题。

假设你的代码使用一个volatile sig_atomic_t flag变量,也许是为了捕获SIGINT ,就像这样(参见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 ... ... 

现在,在你的代码的主体中,你可能想“跑到中断”:

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

直到你开始需要等待一些输入/输出的调用,比如select或者poll ,这没什么问题。 “等待”操作需要等待I / O,但是它需要等待SIGINT中断。 如果你只写:

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

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

你可以尝试写:

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

这会缩小“竞争窗口”,因为如果在“做某些工作”代码时发生中断,那么现在您将检测到中断; 但是仍然存在一个种族,因为在测试变量之后 ,中断可以发生,但是选择或者轮询之前。

解决方法是使用sigprocmask的信号阻塞属性(或者,在POSIX线程代码中, 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()这样的基于事件的东西比任何一个)。

在(p)select和(p)poll之间是一个相当微妙的区别:

对于select来说,在你调用select之前,你必须每次初始化和填充丑陋的fd_set位图,因为select会以“破坏性”的方式就地修改它们。 (poll会区分struct pollfd.events.revents成员)。

选择后,即使大部分的fds没有被监视,整个位图也经常被扫描(通过人/代码)进行事件。

第三,位图只能处理数量小于一定限制的fds(当代实现:介于1024..4096之间),这在规则中可以很容易地达到高分辨率(尽管这样的程序可能会已经使用epoll)。

对于select和pselect之间的差异,接受的答案是不正确的。 它确实描述了sig-handler和select之间的竞争条件是如何产生的,但是如何使用pselect来解决这个问题是不正确的。 它忽略了关于pselect的要点,即等待文件描述符或信号准备就绪。 当其中任一个准备就绪时,pselect返回。仅选择等待文件描述符。 选择忽略信号。 看到这个博客文章的一个很好的工作示例: https : //www.linuxprogrammingblog.com/code-examples/using-pselect-to-avoid-a-signal-race