微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

我有一个应用程序,需要从一个进程发送一个数据stream到多个阅读器,每个阅读器都需要查看它自己的stream拷贝。 这是相当高的速度(100MB / s并不less见),所以我想尽量避免重复。 在我的理想世界中,linux将具有支持多个读取器的命名pipe道,并具有用于常见的单个阅读器情况的快速path。

我想要的东西,提供了一些名称空间隔离的措施(例如:在127.0.0.1广播是开放的任何过程,我相信…)。 Unix域套接不支持广播,反正UDP是“不可靠的”(服务器会丢包而不是阻塞在我的情况下)。 我想我可以创build一个共享内存段,并在那里存储常用的缓冲区,但感觉就像重新开始。 有没有一个规范的方式来做到这一点在Linux?

在Linux上从.NET Core手动调用recvmsg不会返回

使用python从Windows发送string到vmware-ubuntu通过套接

TCP保持活动频率不一致

UDP服务器给分段错误

套接字和ARP(IP邻居)表项

我想我可以创建一个共享内存段,并在那里存储常用的缓冲区,但感觉就像重新开始。 有没有一个规范的方式来做到这一点在Linux?

简短的回答: 没有

长的回答: 是的 [你在正确的轨道上]

我必须在更高的速度之前这样做,所以我必须研究这个。 以下是我想出来的。

在主流程中,创建一个共享缓冲池[按照您的选择使用SysV shm或私有mmap]。 给他们分配身份证号码(如1,2,3,…)。 现在有一个从bufid到缓冲存储器地址的映射。 为了让子进程可以访问它,请在分叉之前执行此操作。 孩子们也继承了共享内存映射,所以没有太多的工作

现在分叉孩子。 给他们每个独特的进程ID。 你可以从一个数字开始递增:2,3,4,… [main是1]或者只是使用普通的pid。

打开一个SysV信息通道( msgget et。al。)。 同样,如果你在分叉之前的主要过程中这样做,他们可以提供给孩子们[IIRC]。

下面是它的工作原理:

主要找到一个未使用的缓冲区并填充它。 对于每个孩子,主要通过msgsnd (在单个通用IPC通道上)发送IPC消息,其中消息有效载荷[ mtext ]是bufid编号。 每个消息都有标准头的mtype字段设置为目标孩子的pid。

这样做后,主要记住缓冲区为“在飞行中”,不能重复使用。

每个孩子都将mtype设置为其pid。 然后从mtext提取bufid并处理缓冲区。 当它完成时,它会发送一个IPC消息[再次在同一个通道上], mtype设置为main的pid,并带有刚刚处理的bufid的mtext 。

main的循环做了一个非阻塞的msgrcv ,注释给定bufid的所有“释放”消息。 当所有的孩子已经释放缓冲区,它被放回缓冲区的“空闲队列”。 在main的服务循环中,它可以填充新的缓冲区,并在适当的时候发送更多的消息[穿插等待]。

小孩然后做一个msgrcv,循环重复。

所以,我们使用[大]共享内存缓冲区和短bufid描述符IPC消息。

好,那么你可能会问的问题是: “为什么SysV IPC用于comm通道? [对比 多个管道或插座]。

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

所以,这是要走的路。 但是,为什么不通过套接字或管道[或共享队列,条件变量,互斥等]发送上述bufid消息?

答案是速度和目标进程的唤醒特性。

对于高度实时的响应,当main发送bufid消息时,您希望目标进程(如果正在休眠) 立即唤醒并开始处理缓冲区。

我检查了linux内核源码, 唯一具有该特性的机制是SysV IPC。 所有其他人都有一个[调度]滞后。

当进程A在进程B已经完成msgrcv的通道上执行msgsnd时,会发生三件事情:

进程B将被调度程序标记为可运行。

[IIRC] B将被移到其调度队列的前面

而且,更重要的是,这会导致所有进程立即重新计划。

B将立即启动[相对于下一个定时器中断或者其他一些进程恰好在睡眠中]。 在一个单一的核心机器上,A将被安置入睡,B将继续运行。

警告:我所有的研究都是在CFS调度员之前的几年完成的,但是我相信上述情况仍然存在。 另外,我使用RT调度程序,如果CFS不按预期工作,这可能是一个可能的选项。

更新:

看看POSIX消息队列的来源,我认为你在System V队列中讨论过的同样的立即唤醒行为正在进行,这给了POSIX兼容性的额外好处。

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

对于计时,你可以建立一个单位测试程序[只使用消息]与nsec时间戳。 我使用TSC邮票,但clock_gettime(CLOCK_REALTIME,...)也可能工作。 邮票出发时间和到达/唤醒时间看。 比较两个SysV和mq

无论是SysV还是mq,你都可能需要通过/ proc / *来增加msgs的最大值,max msg size,max#队列。 认值相对较小。 如果你不这样做,你可能会发现任务被阻塞,等待一个味精,但由于msg队列最大参数被超过,主站不能发送一个[被阻塞]。 我实际上有这样一个bug,所以我改变了我的代码,以启动这些值[它以root身份运行]。 所以,你可能需要做一个RC启动脚本(或任何[atrocIoUs ;-)] systemd等价物)

我看着用我自己的代码中的mq替换SysV。 它没有多对一的返回自由池msg的相同的语义。 在我原来的回答中,我忘了提到需要两个 msg队列:master-to-children(例如工作)和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打开队列或使用定时版本。 这在“关闭”或“重启”阶段被使用(例如,发送一个msg给没有更多数据到达的孩子,他们应该终止[或重新配置等])。 IPC_NowAIT便于在程序启动过程中“排空”一个队列[以消除来自先前调用的陈旧消息],或者在操作期间从先前的配置中消除陈旧消息。

所以,而不是只有两个SysV msg队列来处理任意数量的缓冲区列表,您将需要为每个缓冲区列表/类型单独的mqd_t 。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐