如何在没有泄漏手柄(跨平台)的情况下进行本地IPC?

时间:2014-01-29 03:04:29

标签: python cross-platform ipc resource-leak

如何使用子进程启动IPC,而不让它继承所有句柄?为了使它更有趣,这个shoud在windows和unix上工作。

背景:我正在编写一个与3rparty共享库(我们称之为IT)的库,后者又包含全局数据(实际上应该是对象!)。我想拥有这个全局数据的多个实例。据我了解,我有两种方法可以解决这个问题:

  1. 创建一个链接IT静态变体的cython模块,然后在需要新实例时复制并导入模块。类似地,我可以复制IT,但是创建一个ctypes界面还有更多的工作。

  2. 产生一个加载IT并建立IPC连接的子流程。

  3. 使用(2)有几个原因:

    • 我不确定,如果(1)在任何方面都是可靠的,那感觉就像一个坏主意(当应用程序以不受控制的方式退出时,所有额外模块会发生什么?)。

    • 为了安全考虑,将IT装入一个单独的流程实际上可能是一个好主意:IT处理可能不安全的输入,而IT的代码质量并不是太好。因此,在运行它时,我宁愿不打开任何安全资源。

    • 在未来的应用中,这种IPC可能有很多需要

    那么我的选择是什么?我已经研究过了:

    • multiprocessing.Process起初看起来不错,直到我意识到新进程获得了我所有句柄的副本。毋庸置疑,这是非常有问题的,因为现在通过在父进程中关闭资源+前面提到的安全问题,无法可靠地释放资源。

    • os.closerange内使用multiprocessing.Process手动关闭所有句柄 - 除了我感兴趣的管道。os.closerange仅关闭文件或是否需要小心还有其他类型的资源?如果是这样的话:在给定Pipe对象的情况下,如何确定范围?

    • subprocess.Popen(.., close_fds=True, stdin=PIPE, stdout=PIPE)在unix上运行正常但在win32上无效。

    • 命名管道在win32和unix上非常不同。他们的任何图书馆都在使用它们吗?

    • 套接字。很有希望,特别是因为它们是可以使用套接字的方便的RPC库。另一方面,我担心这可能会导致一大堆安全问题。我确定来自本地的套接字(sock.getpeername()[0] == '127.0.0.1')是否可以防止回火?

    我有没有忽视的可能性?

    要整理:主要问题是如何在Windows + unix上使用子进程建立安全的IPC?但如果您只知道部分问题的答案,请不要犹豫回答。

    感谢您花时间阅读它!

1 个答案:

答案 0 :(得分:0)

似乎在python> = 3.4 subprocess.Popen(..., stdin=PIPE, stdout=PIPE, close_fds=False)是一个可能的选择。这是由于一个补丁使默认情况下所有打开的文件描述符都不可继承。更准确地说,它们将在execv上自动关闭(因此仍然无法使用multiprocessing.Process),请参阅PEP 446

这也是其他python版本的有效选项:

  • 在Windows上,HANDLEs默认情况下创建为不可继承,因此您只会泄漏明确可继承的句柄
  • 在POSIX / python< = 3.3上,你仍然可以使用os.closerange在产生子进程后关闭打开的文件描述符

相应的例子见:

https://github.com/coldfix/python-ipc-test

最有用的组合是:

  1. 标准输入输出:泡菜

    • 亲:在我的测试中完全跨平台
    • 亲:最快的选项(有2个)
    • con: stdin / stdout无法独立重定向
  2. inherit_unidir:泡菜

    • 专业版:您可以独立重定向STDIO流
    • 亲:最快的选项以及stdio:pickle
    • con:非常低级别的平台特定代码
  3. 插座:sockpipe

    • 亲:跨平台,省力;
    • con:“攻击者”可能会在短时间内连接到该端口,您可能需要使用密码或其他内容来防止这种情况发生
    • con:比Windows上的替代品略慢(我测量中的因子1.6)
    • 当不使用AF_UNIX时,linux上有不可预知的性能命中