FIFO(命名管道)消息传递障碍

2 投票
2 回答
6874 浏览
提问于 2025-04-15 11:55

我打算使用Unix命名管道(mkfifo)来进行简单的多进程消息传递。消息就是一行简单的文本。

你会建议我不要这样做吗?我应该预期会遇到哪些困难呢?

我注意到了一些限制:

  1. 发送方在消息被接收之前不能继续发送。
  2. 接收方在没有数据到达之前会被阻塞。我们需要使用非阻塞输入输出,这样才能在需要停止读取时处理。例如,另一个线程可能会请求停止。
  3. 接收方在一次读取中可能会获取到很多消息。这些消息必须在退出之前处理完。
  4. 一个原子消息的最大长度限制为4096字节。这是Linux上的PIPE_BUF限制(可以查看man 7 pipe了解更多)。

我会用Python来实现这个消息传递功能。但这些困难是普遍存在的。

2 个回答

4

在发送方和接收方都可能出现的阻塞问题,可以通过非阻塞输入输出(I/O)来解决。

关于FIFO(先进先出队列)的进一步限制:

  • 一次只能有一个客户端连接。
  • 当客户端关闭FIFO后,服务器需要重新打开它的端点。
  • 数据传输是单向的。

我会推荐使用UNIX域套接字,因为它没有上述的这些限制。

另外一个好处是,如果你想让它能够在多台机器之间进行通信,几乎不需要做任何改动。比如,只需在Python的文档页面上找到socket的例子,把socket.AF_INET换成socket.AF_UNIX,把(HOST, PORT)换成filename,就可以直接使用了。

SOCK_STREAM会让你获得类似流的行为;也就是说,可能会把两次发送合并成一次接收,反之亦然。AF_UNIX也支持SOCK_DGRAM:数据报会保证作为一个整体发送和接收,或者根本不发送。(类似地,AF_INETSOCK_STREAM就是TCP,AF_INETSOCK_DGRAM就是UDP。)

5
  1. 可移植性差 - 主要是在Unix系统上使用,套接字(sockets)更容易在不同系统间使用。
  2. 在多个系统之间扩展比较困难(这时套接字更好用)。
  3. 不过,我觉得在同一台机器上,管道(pipes)的速度比套接字快,因为它们的通信开销更小。

关于你的限制,

  1. 你可以在管道上使用 "select",这样可以进行非阻塞读取。
  2. 我通常在管道中用 "\n" 来分隔我的消息,然后逐行读取,这样每次就能获取一条消息。
  3. 要小心原子长度(atomic length)。

我觉得 perlipc 对各种选项进行了很好的讨论,虽然里面的代码是针对perl的。

撰写回答