规范的方式将数据“广播”到linux中的多个进程?

时间:2016-03-14 19:56:08

标签: linux sockets

我有一个应用程序需要将一个数据流从一个进程发送到多个读取器,每个读取器都需要查看它自己的流副本。这是相当高的速率(100MB / s并不罕见),所以我想尽可能避免重复。在我的理想世界中,linux会命名支持多个读卡器的管道,为普通的单读卡器机箱提供快速通道。

我想要提供一些命名空间隔离度的东西(例如:127.0.0.1上的广播对任何我相信的过程开放......)。 Unix域套接字不支持广播,并且UDP“不可靠”(在我的情况下,服务器将丢弃数据包而不是阻塞)。我想我可以创建一个共享内存段并在那里存储公共缓冲区,但这就像重新发明轮子一样。在linux中有没有规范的方法呢?

1 个答案:

答案 0 :(得分:2)

  

我想我可以创建一个共享内存段并在那里存储公共缓冲区,但这就像重新发明轮子一样。在linux中有没有规范的方法呢?

答案简短:

答案很长: [并且您已走上正轨]

我必须在[更高的速度]之前做到这一点,所以我不得不研究这个。以下是我的想法。

在主过程中,创建一个共享缓冲池[根据您的选择使用SysV shm或私有mmap]。为它们分配ID号(例如1,2,3,......)。现在有一个从bufid到缓冲区内存地址的映射。要使子进程可以访问它,请在分叉之前执行此操作。孩子们也继承了共享内存映射,所以工作量不多

现在分叉孩子们。给每个人一个独特的进程ID。您可以逐步从数字开始:2,3,4,... [main为1]或只使用常规pids。

打开SysV消息通道(msgget等。)。同样,如果你在分叉前的主要过程中这样做,它们可供孩子们使用[IIRC]。

现在,它是如何运作的:

main找到一个未使用的缓冲区并填充它。对于每个子节点,main通过msgsnd(在单个公共IPC信道上)发送IPC消息,其中消息有效负载[mtext]是bufid号。每条消息都将标准标题mtype字段设置为目标子项的pid。

执行此操作后,main会将缓冲区记录为"在飞行中"而且还没有重复使用。

每个孩子都会msgrcv设置{pid} mtype。然后它从mtext中提取bufid并处理缓冲区。完成后,它会[再次在同一频道上]发送一条IPC消息,mtype设置为主要的pid,其中只有mtext刚才处理的bufid。

主循环执行非阻塞msgrcv,注意所有"释放"给定bufid的消息。当所有孩子都释放缓冲区后,它会重新放回缓冲区"空闲队列"。在主要的服务循环中,它可以填充新的缓冲区并根据需要发送更多消息[穿插等待]。

然后孩子做一个msgrcv并重复循环。

因此,我们使用[大]共享内存缓冲区和短[几个字节] bufid描述符IPC消息。

好的,你可能会问的问题是:"为什么SysV IPC用于通讯渠道?" [vs.多个管道或插座]。

您已经知道共享缓冲区可以避免发送数据的多个副本。

所以,这是最佳选择。但是,为什么不在套接字或管道[或共享队列,条件变量,互斥体等]上发送上述bufid消息?

答案是速度和目标过程的唤醒特征。

对于高度实时响应,当main发出bufid消息时,您希望目标进程[如果它正在休眠]立即唤醒 并开始处理缓冲区。 / p>

我检查了linux内核源代码和具有该特性的 only 机制是SysV IPC。所有其他人都有[安排]滞后。

如果流程A在流程B已完成msgsnd的渠道上执行msgrcv,则会发生以下三种情况:

  1. 进程B将被调度程序标记为可运行。
  2. [IIRC] B将移至其调度队列的前面
  3. 此外,更重要的是,这会导致所有进程的立即重新安排。
  4. B将立即启动[与下一个定时器中断相反或当其他一些进程恰好睡眠时]。在单核心机器上,A将进入睡眠状态,B将依次运行。

    警告:我所有的研究都是在CFS调度程序之前几年完成的,但我相信上述内容仍然应该成立。此外,我正在使用RT调度程序,如果CFS没有按预期工作,这可能是一个可能的选项。

    <强>更新

      

    查看POSIX消息队列源,我认为您与System V队列讨论的相同的立即唤醒行为正在进行,这提供了POSIX兼容性的额外好处。

    定时语义是可能的[并且是可取的]所以我不会感到惊讶。但是,SysV实际上比POSIX mqueues更标准和无处不在。并且, 存在一些语义差异[见下文]。

    对于计时,您可以使用nsec时间戳构建单元测试程序[仅使用msgs]。我使用过TSC标记,但clock_gettime(CLOCK_REALTIME,...)也可以使用。邮票出发时间和到达/唤醒时间看。比较SysV和mq

    使用SysV或mq,您可能需要通过/ proc / *提高最大消息数量,最大消息数量,最大队列数量。默认值相对较小。如果你没有,你可能会发现任务被阻塞等待一个msg但是由于超过了msg队列最大参数,master无法发送一个[被阻止]。我实际上有这样的错误,所以我改变了我的代码以在启动期间提升这些值[它以root身份运行]。因此,您可能需要将此作为RC启动脚本(或者等于[atrocious ;-)]系统等效的任何内容)

    我查看了使用mq在我自己的代码中替换SysV。对于多对一返回到自由池的消息,它没有相同的语义。在我原来的答案中,我忘了提到需要两个 msg队列:master-to-children(例如work-to-do)和child-to-master(例如返回一个现在可用的缓冲区) )。

    我有几种不同类型的缓冲区(例如压缩视频,压缩音频,未压缩视频,未压缩音频),它们具有不同的类型和结构描述符。

    此外,多个不同的缓冲区队列作为这些缓冲区从线程传递到线程[不同的处理阶段]。

    使用SysV,您可以将单个msg队列用于多个缓冲区列表/队列,缓冲区列表ID是msg mtype。子msgrcv等待mtype设置为ID值。主设备等待返回到空闲的消息队列,mtype为0。

    mq *要求每个ID单独mqd_t,因为它不允许等待msg子类型。

    msgrcv允许每次调用IPC_NOWAIT,但为了与mq_receive获得相同的效果,您必须使用O_NONBLOCK打开队列或使用定时版本。这在&#34;关闭期间被使用&#34;或者&#34;重启&#34;阶段(例如向孩子发送一个msg,不再有数据到达,他们应该终止[或重新配置等])。 IPC_NOWAIT对于&#34;排水&#34;非常方便程序启动期间的队列[从先前的调用中删除过时的消息]或在操作期间从先前的配置中排出过时的消息。

    因此,代替只有两个SysV msg队列来处理任意数量的缓冲区列表,您需要为每个缓冲区列表/类型单独mqd_t