C 应用程序和 Python 之间的 IPC

IPC between C application and Python

所以我对 IPC 比较陌生,我有一个收集数据的 c 程序和一个分析数据的 python 程序。我希望能够:

  1. 调用 python 程序作为我的主 c 程序的子进程
  2. 将包含要处理的数据的 c 结构传递给 python 进程
  3. Return 从 python 进程返回到 c 程序的 int 值

我一直在简要地查看 Pipes 和 FIFO,但到目前为止找不到任何信息来解决此类问题,因为据我了解,例如 fork() 只会复制调用过程,所以不会当我尝试调用不同的进程时,我想要什么。

您组织体系结构的方式有点混乱。你真正想要的是Message Queues。所以在你的例子中:

  • 您的 python 工作人员侦听队列 A 中要处理的新信息;
  • 你的C程序在队列A中输入数据;
  • 您的 python 工作人员处理数据并将结果排队到队列 B;
  • 您的 C 程序侦听队列 B 上的新项目;

这可能会有所不同,但概念很简单。

它们易于实施,并且有大量的库和工具可以帮助您完成这项任务。 ZeroMQ 当然可以。它适用于 C 和 Python.

关于fork()需要执行不同的流程。 fork() 确实创建了当前进程的副本。但这通常与exec()(各种形式之一)结合使用,以获取进程副本以执行不同的程序。

至于IPC,你有几个选择。有人提到了队列——但是像 ZeroMQ 这样的东西有点矫枉过正。您可以 IPC 使用几种机制中的一种。

  1. Pipes(命名管道或匿名)
  2. Unix 域套接字
  3. TCPUDP 通过插座 API
  4. Shared memory
  5. Message queues

pipe 方法最简单。请注意,当您在 C 程序和 Python 之间来回传递数据时,您需要担心数据的传输语法。如果您选择使用 C 结构(不可移植),则需要在 Python 端解压数据。否则你可以使用一些文本格式 - sprintf/sscanfJSON 等的组合

如果你的结构足够简单,你甚至可以完全不使用 IPC。提供,你可以序列化它作为字符串参数,可以用作程序参数,并提供给return的int值可以在0-127范围内,你可以简单地:

  • 在 C 代码中:

    • 准备要传递给 Python 脚本的命令参数
    • fork-exec(假设一个 Unix-like 系统)一个 Python 带有脚本路径和脚本参数的解释器
    • 等待child终止
    • 读取脚本作为代码终止传递的内容
  • 在 Python:

    • 从命令行获取参数并重建结构的元素
    • 处理它
    • exit(n) 结束脚本,其中 n 是 0-127 范围内的整数,将 returned 给调用者。

如果以上不能满足您的要求,下一级将使用管道:

  • 在 C 代码中:

    • 准备 2 对管道,一对用于 C->Python(我们称之为输入),一对用于 Python->C(我们称之为输出)
    • 将结构序列化为字符缓冲区
    • 分叉
    • 在child
      • 关闭输入管道的写入端
      • 关闭输出管道的读取端
      • dup 输入管道的读取端到文件描述符 0 (stdin)(参见`dup2)
      • dup 将输出管道的写入端写入文件描述符 1 (stdout)
      • 用脚本的名字执行一个Python解释器
    • 在parent
      • 关闭输入管道的读取端
      • 关闭输出管道的写入端
      • 将缓冲区(如果无法先验知道,则最终以其大小开头)写入输入文件的写入端
      • 等待 child 终止
      • 从输出管道的读取端读取 return 值
  • 在 Python:

    • 从标准输入读取序列化数据
    • 处理它
    • 将输出整数写入标准输出
    • 退出

我建议查看应用程序并构建您面临的问题。

多线程

到目前为止,启动两个进程并不是最大的问题,正如 Ziffusion 所说,您可以让另一个进程做其他事情。另外还有 python C 的绑定,所以你可以创建另一个线程(不需要它是一个进程)并从 C 程序调用你的 python 例程。

通讯

共享信息更有趣,因为您必须解决两个问题:一个是从技术上将数据从一个地方获取到另一个地方,反之亦然;另一个是两个不同的事物如何处理相同的数据。 这涉及消息传递模式和流程:

  • 谁生成数据?
  • 谁接收数据?
  • 是否有一段代码在继续之前等待某些东西?
  • 在处理数据时是否需要控制数据发生的变化?
  • 我想自己编码吗?
  • 我可以在项目中使用库吗?
  • 有安全限制吗?
  • ...

回答上述问题后,您就可以定义应用程序的各个部分将如何交互。一个主要区别是同步与异步。

同步与异步

同步意味着对于每条消息都有一个回复,该回复应包含在有限(通常尽可能小)大小的时间包络中。这是为了避免延迟。 当您必须精确控制正在发生的事情,或者您需要及时 问题answer 时,最好使用此模式。 实际上,这就是 http 下载网页的方式:无论何时加载网站,您都希望立即看到内容。 这是一个名为 REQuest/REPly

的模式

异步通常用于处理繁重的情况:数据生产者(例如数据库接口或传感器)将大量数据发送到工作线程,而不等待答案。工作线程然后开始处理数据,完成后将结果发送到数据 sink/user。 这种模式叫做 PUBlish/SUBscribe.

还有很多其他的,但这些构成了交流的基础。

编组

您面临的另一个问题是如何构造数据传递、编组。 如何将数据的含义和内容从一种情况转移到完全不同的情况。 例如,从您的 C 部分到您的 Python 部分。 维护序列化库既乏味又危险,更不用说容易出现向后兼容性问题了。

实施

当您开始实施时,您通常需要最干净、最强大的代码。这两件事显然是相互对立的。所以我通常会去寻找一个可以完全满足我需要的库。 在这种情况下,我的建议是尝试 ZeroMQ:它精简、灵活、低级。 它将为您提供一个强大的框架来连接线程、进程甚至机器。 ZeroMQ 提供了link,但是你仍然需要一个协议来运行 over this link。 为了避免令人头疼的问题并简化您在编组问题方面的工作,我建议您研究可用的编组库来简化此任务。 Cap'n proto, Flatbuffers,协议缓冲区(Google,还不能 post 超过 2 links) 它们使您可以轻松地用中间语言定义您的数据,并从任何其他语言解析它,而无需您自己编写所有 类。

至于管道和共享内存,我的拙见是:忘记它们的存在。