Python多进程在OSX和Ubuntu上的“写时复制”行为不同

15

我正在尝试在Python中在父进程和子进程之间共享对象。为了玩弄这个想法,我创建了一个简单的Python脚本:

from multiprocessing import Process
from os import getpid

import psutil

shared = list(range(20000000))

def shared_printer():
    mem = psutil.Process(getpid()).memory_info().rss / (1024 ** 2)
    print(getpid(), len(shared), '{}MB'.format(mem))

if __name__ == '__main__':
    p = Process(target=shared_printer)
    p.start()
    shared_printer()
    p.join()

这段代码片段使用了优秀的psutil库来打印RSS(驻留集大小)。当我在带有Python 2.7.15的OSX上运行它时,我得到以下输出:

(33101, 20000000, '1MB')
(33100, 20000000, '626MB')

当我在Ubuntu上运行完全相同的代码片段(Linux 4.15.0-1029-aws#30-Ubuntu SMP x86_64 GNU / Linux),我会得到以下输出:

(4077, 20000000, '632MB')
(4078, 20000000, '629MB')
注意,在OSX上,子进程的RSS基本为0MB,而在Linux中,子进程的RSS大小与父进程的相同。我曾经认为,写时复制行为在Linux中也会以同样的方式工作,并允许子进程引用父进程的大多数页面内存(也许除了存储对象头的页面)。因此,我猜想这两个系统在写时复制行为上存在某些差异。我的问题是:在Linux中是否有任何方法可以实现类似于OSX的写时复制行为?

请查看该问题的顶部答案:https://dev59.com/IXM_5IYBdhLWcg3wslbs - Tomasz Swider
4
可能只是操作系统报告 RSS 值的方式不同?除了查看进程的内存外,还需要在 fork 前/后检查可用内存,并观察是否消失了 600MB。 - Perette
1
请查看这个答案:https://dev59.com/oGsz5IYBdhLWcg3weHkA#21049737 - georgexsh
@mat.viguier:您的评论和链接似乎没有关联。静态链接是关于C库的,预编译模块只是改变了.pyc文件是在安装时还是在运行时生成,但无论哪种方式,它都不应该影响fork的行为。 - ShadowRanger
1
@ShadowRanger 我的第一反应也是关于GC的,但行为一致。我认为这与XNU系统报告内存的方式有关。请参见此问题上的host_statistics(由psutil使用)以及答案,解释了为什么其结果在不同平台上不一致。 - sytech
显示剩余3条评论
1个回答

4
我猜测在这两个系统中,写入时复制的行为有所不同。我的问题是:在Linux中是否有任何方法可以获得类似OSX的写时复制行为? 答案是没有。在命令psutil.Process(getpid()).memory_info().rss / (1024 ** 2)后面,操作系统使用UNIX命令$top [PID]并搜索字段RES。其中包含任务在kb中使用的未交换物理内存。即RES = CODE + DATA。
我认为,这意味着两个操作系统使用了不同的内存管理器。因此,几乎不可能限制进程使用/需要的内存量。这是操作系统的内部问题。在Linux中,子进程具有与父进程相同的大小。实际上,它们复制相同的堆栈、代码和数据。但是拥有不同的PCB(进程控制块)。因此,要接近OSX的0几乎是不可能的。看起来OSX并没有完全复制代码和数据。如果它们是相同的代码,则会将指针指向父进程的数据。
注:希望这能对你有所帮助!

1
Linux和OSX都应该使用相同的fork,对于在子进程从父进程分叉时存在的内存具有相同的写时复制语义。我更倾向于怀疑OSX如何计算内存的差异,而不是它使用了多少内存。两者在fork之后几乎没有真正的内存使用,但Linux可能会报告内存使用情况,假设所有写时复制页面最终都被复制(由于CPython的引用计数+循环收集器设计,大多数可能会),而OSX仅报告唯一的私有,非写时复制页面。 - ShadowRanger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接