Linux中的消息队列已经过时了吗?

80

最近我一直在Linux上使用消息队列(System V,但POSIX也可以),它们似乎非常适合我的应用程序,但是在阅读Unix编程艺术之后,我不确定它们是否真的是一个好的选择。

http://www.faqs.org/docs/artu/ch07s02.html#id2922148

System V IPC的上层消息传递层在很大程度上已经不再使用。下层由共享内存和信号量组成,在需要在同一台机器上运行的进程之间进行互斥锁定和一些全局数据共享的情况下仍具有重要应用价值。这些System V共享内存设施演变成了POSIX共享内存API,在Linux、BSD、MacOS X和Windows下得到支持,但不支持经典的MacOS。

http://www.faqs.org/docs/artu/ch07s03.html#id2923376

System V IPC功能在Linux和其他现代Unix系统中都存在。然而,由于它们是一个遗留功能,使用不太频繁。截至2003年中期,Linux版本仍然存在已知的缺陷。似乎没有人足够关心去修复它们。

最近的Linux版本中,System V消息队列是否仍然存在漏洞?我不确定作者是否意味着POSIX消息队列应该是ok的?

看起来套接字是几乎所有事情的首选IPC,但我无法想象如何使用套接字或其他东西实现非常简单的消息队列。还是我想得太复杂了?

我不知道我正在使用嵌入式Linux是否相关?

5个回答

90

就我个人而言,我非常喜欢消息队列,认为它们是 Unix 世界中最被低估的 IPC(进程间通信)方式。它们快速且易于使用。

以下是一些想法:

  • 其中一部分原因只是时尚问题。旧事物又重新被发掘。在消息队列上添加闪亮的小玩意儿,它们就可能成为明年最新、最热门的东西。看看谷歌的 Chrome 使用单独的进程而不是线程来处理标签页。突然之间人们会很兴奋,因为当一个标签页死锁时,它不会导致整个浏览器崩溃。

  • 共享内存有着某种强壮的声誉。如果你没有从机器中挤出最后一点点性能,你就不是一个“真正”的程序员,而 MQs 的效率略低于共享内存。对于许多应用程序而言,这完全是胡说八道,但有时候一旦形成思维定势就难以改变。

  • 消息队列并不适合于具有无限数据的应用程序。流式机制,如管道或套接字,对于这种情况更加容易使用。

  • System V 版本确实已经不再流行。一般情况下,尽量使用 POSIX 版本的 IPC。


3
7年后,希望这个问题仍然有一定的相关性:我想知道在 Ubuntu 14.04linux 3.13 上消息队列的默认设置,即命令 cat /proc/sys/fs/mqueue/msg_max 列出的队列中的10个消息和 /proc/sys/fs/mqueue/msgsize_max 的值为8192字节,它们似乎很小。这些默认设置是否有严格的原因或只是比较老?(man mq_overview 显示 msg_max 的硬限制约为32768,这已经相当高了。)我并不想创建一个无限流式的队列,但是将 msg_max 设置为100-1000 是否可以呢? - xealits
2
有点晚了,但是:当我使用POSIX队列时,队列中有5-100条消息,我没有遇到任何问题。@xealits - Xunnamius

19

是的,我认为消息队列适用于某些应用程序。 POSIX 消息队列提供了更好的接口,特别是你可以给你的队列命名而不是使用ID,这对故障诊断非常有用(使得更容易区分哪个是哪个)。

Linux 允许你将 posix 消息队列挂载为文件系统,并使用 "ls" 查看它们,使用 "rm" 删除它们,这也非常方便(System V 依赖于笨重的 "ipcs" 和 "ipcrm" 命令)。


1
此功能也适用于UNIX数据报套接字。我可以将数据报套接字绑定到文件,并像POSIX消息队列一样接收。我甚至可以使用“netcat”将测试数据发送到数据报套接字文件。 - KRoy

14

POSIX消息队列的最大缺点:

  • POSIX消息队列不要求与select()兼容。(它在Linux中与select()一起工作,但在Qnx系统中不行。)
  • 它有惊喜。

Unix数据报套接字可以完成与POSIX消息队列同样的任务,并且可以在套接字层工作。可以使用select()/poll()或其他IO等待方法。在设计基于事件的系统时,使用select()/poll()具有优势。这种方式可以避免忙等待。

消息队列中存在意外。想想mq_notify()。它用于获取接收事件。听起来好像我们可以通知某些关于消息队列的事情。但实际上它只是注册通知而已,而非真正地通知任何内容。

mq_notify()的更多惊喜是,它必须在每次mq_receive()之后调用,这可能会导致竞争条件(当其他一些进程/线程在调用mq_receive()mq_notify()之间调用mq_send()时)。

它还有一个完整的mq_open,mq_send(),mq_receive()和mq_close()集合,具有自己的定义,这是多余的,并且在某些情况下与套接字open(),send(),recv()和close()方法规范不一致。

我认为消息队列不应该用于同步。对此,eventfdsignalfd是合适的。

但是它(POSIX消息队列)具有一些实时支持,它具有优先级特性。

Messages are placed on the queue in decreasing order of priority, with newer messages of the same priority being placed after older messages with the same priority.

但是这种优先级也适用于作为带外数据的套接字!

总之,对我来说,POSIX消息队列是一种遗留的API。只要不需要实时特性,我始终更喜欢使用Unix数据报套接字而不是POSIX消息队列。


14

我并未实际使用过POSIX消息队列,因为我总是希望保持将我的消息分布到网络上的选项。考虑到这一点,您可以考虑更强大的消息传递接口,例如 zeromq 或者实现了AMQP的其他工具。

ZeroMQ 的一个优点在于,在多线程应用程序中从相同的进程空间使用时,它使用无锁零拷贝机制,速度非常快。不过,您也可以使用相同的接口通过网络传递消息。


3
消息队列非常有用,可用于构建本地解耦应用程序。它们速度非常快,块组织(不需要缓冲、切割等,这是流式套接字的情况),基本上只有少量的memcpy()操作(用户代码将块复制到内核,内核将块复制到从q读取的其他进程),这就是消息传递的故事。一些行业知名的中间件,如Oracle Tuxedo或Mavimax Enduro/X使用这些队列来帮助构建负载平衡、高性能、容错分解、分布式应用程序。这些队列允许进行负载平衡,当多个可执行文件从同一个队列中读取时,内核调度程序会将消息分配给空闲的进程。对于Linux来说,好处在于可以在Posix队列上进行轮询,这有助于解决某些场景。对于IBM AIX,也可以在System V队列上进行轮询。

例如,两个进程可以通过队列轻松地进行本地通信,并获得相当惊人的吞吐量(约70k req+rply/sec):

Message sending and reply via queues / performance

如果需要进行网络连接,例如Enduro/X提供了tpbridge进程,它基本上从本地队列中读取消息,将块发送到其他机器,在那里另一端将消息注入本地队列。
此外,与套接字相比,您不会遇到任何与队列有关的问题,例如当某些二进制文件崩溃时出现繁忙/滞留套接字的情况,即在启动时程序可以立即开始读取队列并进行处理。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接