【问题标题】:how to write to shared memory in python from stream?如何从流中写入python中的共享内存?
【发布时间】:2012-11-21 05:51:28
【问题描述】:
我有一个来自几台服务器的进程,每秒通过 udp 向我的本地端口 2222 发送数据。
我想读取这些数据并将其写入共享内存,以便其他进程可以从共享内存中读取数据并对其执行操作。
我一直在阅读有关 mmap 的信息,看来我必须使用一个文件...我似乎无法理解为什么。
我有一个从套接字读取数据的a.py,但是如何将其写入 shm?
写完后,我需要写b.py、c.py、d.py 等,以读取相同的 shm 并对其执行操作。
任何帮助或sn-p代码都会有很大帮助。
【问题讨论】:
标签:
python
sockets
shared-memory
【解决方案1】:
首先,请注意,您要构建的内容不仅仅需要共享内存:如果a.py 写入共享内存就很好,但是b.py 将如何知道内存何时准备就绪并且可以读取从?总而言之,通过连接多个进程而不是通过共享内存,而是通过一些其他机制来解决这个问题通常更简单。
(mmap通常需要一个文件名的原因是它需要一个名称来连接几个进程。确实,如果a.py和b.py都调用mmap() , 系统怎么知道这两个进程要求共享内存,而不是一些无关的z.py?因为它们都mmaped 同一个文件。还有Linux特定的扩展名这不对应于 文件 名称,但它更像是一个 hack 恕我直言。)
也许最基本的替代机制是管道:它们通常在程序启动时在shell的帮助下连接。这就是以下工作的方式(在 Linux/Unix 上):python a.py | python b.py。 a.py 发送的任何输出都会进入管道,管道的另一端是b.py 的输入。您将编写 a.py 以便它侦听 UDP 套接字并将数据写入标准输出,并编写 b.py 以便它从标准输入读取以处理接收到的数据。如果数据需要进入多个进程,您可以使用例如命名管道,它有一个很好的(但特定于 Bash 的)语法:python a.py >(python b.py) >(python c.py) 将以两个参数开始 a.py,它们是可以打开和写入的伪文件的名称。写入第一个伪文件的内容作为b.py 的输入,同样,写入第二个伪文件的内容作为c.py 的输入。
【解决方案2】:
mmap 不采用文件名,而是采用文件描述符。它执行所谓的内存映射,即将进程的虚拟内存空间中的页面与文件描述符表示的类文件对象的部分相关联。这是一个非常强大的操作,因为它允许您:
- 简单地将文件的内容作为内存中的数组来访问;
- 访问特殊 I/O 硬件的内存,例如声卡的缓冲区或图形适配器的帧缓冲区(这是可能的,因为 Unix 中的文件描述符是抽象的,它们也可以引用设备节点而不是常规文件);
- 通过执行同一对象的共享映射在进程之间共享内存。
在 Unix 上使用共享内存的旧的 pre-POSIX 方法是使用 System V IPC 共享内存。首先必须使用shmget(2) 创建一个共享内存段,然后使用shmat(2) 附加到进程。 SysV 共享内存段(以及其他 IPC 对象)没有名称,而是数字 ID,因此提供了特殊的哈希函数 ftok(3),它将路径名字符串和项目 ID 整数的组合转换为数字键 ID,但碰撞是可能的。
使用共享内存的现代 POSIX 方法是使用shm_open(2) 打开一个类似文件的内存对象,使用ftruncate(2) 将其调整为所需的大小,然后使用mmap(2) 它。在这种情况下,内存映射的作用类似于来自 SysV IPC API 的 shmat(2) 调用,并且由于 shm_open(2) 创建初始大小为零的对象,因此需要截断。
(这些是 C API 的一部分;Python 模块提供的或多或少是围绕这些调用的瘦包装器,并且通常具有几乎相同的签名)
也可以通过在所有需要共享内存的进程内存映射同一个常规文件来获得共享内存。事实上,Linux 通过在特殊的tmpfs 文件系统上创建文件来实现 POSIX 共享内存操作。 tmpfs 驱动程序通过将保存文件内容的页面直接映射到执行mmap(2) 的进程的地址空间来实现非常轻量级的内存映射。由于tmpfs 的行为与普通文件系统一样,您可以使用ls、cat 和其他shell 工具检查其内容。您甚至可以通过这种方式创建共享内存对象或修改现有对象的内容。 tmpfs 中的文件与常规文件系统文件之间的区别在于,后者被持久化到存储介质(硬盘、网络存储、闪存驱动器等)中,并且偶尔会将更改刷新到该存储介质中,而前者则完全存在在内存中。 Solaris 也提供类似的基于 RAM 的文件系统,也称为tmpfs。
在现代操作系统中,内存映射被广泛使用。可执行文件是内存映射的,以提供这些页面的内容,这些页面包含可执行代码和静态数据。共享库也是内存映射的。这可以节省物理内存,因为这些映射是共享的,例如保存可执行文件或共享库内容的同一物理内存映射到每个进程的虚拟内存空间中。