【问题标题】:are posix pipes lightweight?posix管道是轻量级的吗?
【发布时间】:2011-02-01 18:15:56
【问题描述】:

在一个 linux 应用程序中,我使用管道在线程之间传递信息。

使用管道背后的想法是我可以使用 poll(2) 一次等待多个管道。这在实践中效果很好,而且我的线程大部分时间都在休眠。他们只有在有事可做时才会醒来。

在用户空间中,管道看起来就像两个文件句柄。现在我想知道这样的管道在操作系统端使用了多少资源。

顺便说一句:在我的应用程序中,我不时地只发送单个字节。将我的管道想象成简单的消息队列,允许我唤醒接收线程,告诉它们发送一些状态数据或终止。

【问题讨论】:

  • Posix 指定的是管道接口,而不是实现,这就是“权重”的来源。
  • 多少频道?每秒多少条消息?数百?百万?使用多种技术(从 IPC 消息队列到管道到 UNIX 套接字等)制作一个简单的原型并进行比较。

标签: c linux posix pipe


【解决方案1】:

不,我不会认为管道“轻量级”,但这并不一定意味着它们对于您的应用程序来说也是错误的答案。

通过管道发送一个字节至少需要 3 次系统调用(写入、轮询、读取)。使用内存队列和 pthread 操作(mutex_lock、cond_signal)涉及的开销要少得多。打开文件描述符肯定会消耗内核资源;这就是为什么默认情况下进程通常限制为 256 个打开的文件(并不是说限制不能在适当的地方扩大)。

不过,用于线程间通信的管道/轮询解决方案确实也有优势:尤其是当您需要等待来自多个源(网络 + 其他线程)组合的输入时。

【讨论】:

  • 为什么需要 3 次系统调用?如果管道的缓冲区已满,则盲目的 write() 调用将阻塞。如果每个线程只需要监听自己的管道,你也可以放弃poll(),等待read()成功
  • OP 明确表示他正在使用 poll() 来等待来自一个消费者线程的多个管道。
  • 我必须读取和写入一个文件(/dev/ttySxx 串行)并且能够响应来自另一个线程的命令。阻塞是不可能的。例如,如果我在串行上有一个 10 兆字节的写入,如果应用程序告诉我它要中止传输,我就不能等到它完成。因此,我轮询了两个非阻塞文件:一个用于串行端口,一个用于接收命令。
  • 值得注意的是,如果您希望 poll 等待其他事情,那么您几乎必须使用管道/eventfd,因为您不能将 POSIX 同步原语与其他任何东西混合(即使使用套接字)。如果您生成一个新线程来处理网络事件并将它们转换为 POSIX 互斥锁等,那么您可能已经将它们的性能优势抛到了窗外。
【解决方案2】:

当您使用 Linux 时,您可以调查并比较 pipeeventfd 的性能。从技术上讲,它们速度更快、重量更轻,但你会很幸运能在实践中看到实际收益。

http://www.kernel.org/doc/man-pages/online/pages/man2/eventfd.2.html

【讨论】:

  • 哇!感谢您指出了这一点。我正在使用管道来处理这类事情,而且我确信我可以使用 eventfd 替换其中的至少一半。大多数时候,我真正想要的只是向在轮询中阻塞的线程发出状态信号。如果 eventfd 更轻量级,我会使用它。
【解决方案3】:

测量一下就知道了。 使用管道的完整流程对于许多应用来说都足够轻量级。其他应用程序需要更轻量级的东西,比如操作系统线程(pthreads 是许多 Unix 应用程序的流行选择),或者超轻量级,比如除了处理 I/O 之外永远不会进入内核模式的用户级线程包。虽然确定的唯一方法是测量,但管道可能足以支持多达几十个线程,而一旦达到几万个线程,您可能需要用户级线程。我不知道使用今天的代码应该在哪里绘制边界。如果我想知道,我会测量:-)

【讨论】:

  • 我想我很困惑。我认为 Linux 的 NPTL 实现是内核级别的。 glibc 是否在此之上提供用户级实现?
猜你喜欢
  • 2020-07-16
  • 2010-09-29
  • 2011-06-16
  • 1970-01-01
  • 2012-06-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多