代码之家  ›  专栏  ›  技术社区  ›  davidmytton

蟒蛇子流程.Popen“OSError:[Errno 12]无法分配内存”

  •  92
  • davidmytton  · 技术社区  · 16 年前

    这个问题最初是被问到的 here 但是悬赏期已经过了,尽管还没有找到一个可以接受的答案。我重新问这个问题,包括在原来的问题中提供的所有细节。

    python脚本使用 sched 模块:

    # sc is a sched.scheduler instance
    sc.enter(60, 1, self.doChecks, (sc, False))
    

    脚本正在使用以下代码作为守护进程运行 here .

    作为docheck的一部分调用的许多类方法使用 subprocess 调用系统函数以获取系统统计信息的模块:

    ps = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE).communicate()[0]
    

    File "/home/admin/sd-agent/checks.py", line 436, in getProcesses
    File "/usr/lib/python2.4/subprocess.py", line 533, in __init__
    File "/usr/lib/python2.4/subprocess.py", line 835, in _get_handles
    OSError: [Errno 12] Cannot allocate memory
    

    脚本崩溃后,服务器上free-m的输出为:

    $ free -m
                      total       used       free     shared     buffers    cached
    Mem:                894        345        549          0          0          0
    -/+ buffers/cache:  345        549
    Swap:                 0          0          0
    

    服务器正在运行CentOS 5.3。我无法复制在我自己的CentOS盒,也没有任何其他用户报告相同的问题。

    我已经尝试了很多方法来调试这个问题,正如在最初的问题中所建议的:

    1. 在Popen调用前后记录free-m的输出。内存使用没有明显变化,即在脚本运行时内存没有逐渐用完。

    2. 我在Popen调用中添加了close_fds=True,但这没有什么区别-脚本仍然崩溃并出现相同的错误。建议 here here .

    3. 我检查了rlimits,它在RLIMIT_数据和RLIMIT_上都显示了(-1,-1) here .

    4. An article 建议没有交换空间可能是原因,但交换实际上是按需提供的(根据网络主机),这也被认为是一个虚假的原因 here

    5. 进程正在关闭,因为这是使用Python源代码和注释备份的.communicate()的行为 here .

    全部支票可在上找到 GitHub here 使用第442行定义的getprocesss函数。这由doChecks()从第520行开始调用。

    recv(4, "Total Accesses: 516662\nTotal kBy"..., 234, 0) = 234
    gettimeofday({1250893252, 887805}, NULL) = 0
    write(3, "2009-08-21 17:20:52,887 - checks"..., 91) = 91
    gettimeofday({1250893252, 888362}, NULL) = 0
    write(3, "2009-08-21 17:20:52,888 - checks"..., 74) = 74
    gettimeofday({1250893252, 888897}, NULL) = 0
    write(3, "2009-08-21 17:20:52,888 - checks"..., 67) = 67
    gettimeofday({1250893252, 889184}, NULL) = 0
    write(3, "2009-08-21 17:20:52,889 - checks"..., 81) = 81
    close(4)                                = 0
    gettimeofday({1250893252, 889591}, NULL) = 0
    write(3, "2009-08-21 17:20:52,889 - checks"..., 63) = 63
    pipe([4, 5])                            = 0
    pipe([6, 7])                            = 0
    fcntl64(7, F_GETFD)                     = 0
    fcntl64(7, F_SETFD, FD_CLOEXEC)         = 0
    clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0xb7f12708) = -1 ENOMEM (Cannot allocate memory)
    write(2, "Traceback (most recent call last"..., 35) = 35
    open("/usr/bin/sd-agent/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
    open("/usr/bin/sd-agent/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
    open("/usr/lib/python24.zip/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
    open("/usr/lib/python2.4/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
    open("/usr/lib/python2.4/plat-linux2/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
    open("/usr/lib/python2.4/lib-tk/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
    open("/usr/lib/python2.4/lib-dynload/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
    open("/usr/lib/python2.4/site-packages/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
    write(2, "  File \"/usr/bin/sd-agent/agent."..., 52) = 52
    open("/home/admin/sd-agent/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
    open("/usr/bin/sd-agent/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
    open("/usr/lib/python24.zip/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
    open("/usr/lib/python2.4/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
    open("/usr/lib/python2.4/plat-linux2/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
    open("/usr/lib/python2.4/lib-tk/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
    open("/usr/lib/python2.4/lib-dynload/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
    open("/usr/lib/python2.4/site-packages/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
    write(2, "  File \"/home/admin/sd-agent/dae"..., 60) = 60
    open("/usr/bin/sd-agent/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
    open("/usr/bin/sd-agent/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
    open("/usr/lib/python24.zip/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
    open("/usr/lib/python2.4/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
    open("/usr/lib/python2.4/plat-linux2/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
    open("/usr/lib/python2.4/lib-tk/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
    open("/usr/lib/python2.4/lib-dynload/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
    open("/usr/lib/python2.4/site-packages/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
    write(2, "  File \"/usr/bin/sd-agent/agent."..., 54) = 54
    open("/usr/lib/python2.4/sched.py", O_RDONLY|O_LARGEFILE) = 8
    write(2, "  File \"/usr/lib/python2.4/sched"..., 55) = 55
    fstat64(8, {st_mode=S_IFREG|0644, st_size=4054, ...}) = 0
    mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7d28000
    read(8, "\"\"\"A generally useful event sche"..., 4096) = 4054
    write(2, "    ", 4)                     = 4
    write(2, "void = action(*argument)\n", 25) = 25
    close(8)                                = 0
    munmap(0xb7d28000, 4096)                = 0
    open("/usr/bin/sd-agent/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
    open("/usr/bin/sd-agent/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
    open("/usr/lib/python24.zip/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
    open("/usr/lib/python2.4/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
    open("/usr/lib/python2.4/plat-linux2/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
    open("/usr/lib/python2.4/lib-tk/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
    open("/usr/lib/python2.4/lib-dynload/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
    open("/usr/lib/python2.4/site-packages/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
    write(2, "  File \"/usr/bin/sd-agent/checks"..., 60) = 60
    open("/usr/bin/sd-agent/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
    open("/usr/bin/sd-agent/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
    open("/usr/lib/python24.zip/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
    open("/usr/lib/python2.4/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
    open("/usr/lib/python2.4/plat-linux2/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
    open("/usr/lib/python2.4/lib-tk/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
    open("/usr/lib/python2.4/lib-dynload/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
    open("/usr/lib/python2.4/site-packages/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
    write(2, "  File \"/usr/bin/sd-agent/checks"..., 64) = 64
    open("/usr/lib/python2.4/subprocess.py", O_RDONLY|O_LARGEFILE) = 8
    write(2, "  File \"/usr/lib/python2.4/subpr"..., 65) = 65
    fstat64(8, {st_mode=S_IFREG|0644, st_size=39931, ...}) = 0
    mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7d28000
    read(8, "# subprocess - Subprocesses with"..., 4096) = 4096
    read(8, "lso, the newlines attribute of t"..., 4096) = 4096
    read(8, "code < 0:\n        print >>sys.st"..., 4096) = 4096
    read(8, "alse does not exist on 2.2.0\ntry"..., 4096) = 4096
    read(8, " p2cread\n        # c2pread    <-"..., 4096) = 4096
    write(2, "    ", 4)                     = 4
    write(2, "errread, errwrite)\n", 19)    = 19
    close(8)                                = 0
    munmap(0xb7d28000, 4096)                = 0
    open("/usr/lib/python2.4/subprocess.py", O_RDONLY|O_LARGEFILE) = 8
    write(2, "  File \"/usr/lib/python2.4/subpr"..., 71) = 71
    fstat64(8, {st_mode=S_IFREG|0644, st_size=39931, ...}) = 0
    mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7d28000
    read(8, "# subprocess - Subprocesses with"..., 4096) = 4096
    read(8, "lso, the newlines attribute of t"..., 4096) = 4096
    read(8, "code < 0:\n        print >>sys.st"..., 4096) = 4096
    read(8, "alse does not exist on 2.2.0\ntry"..., 4096) = 4096
    read(8, " p2cread\n        # c2pread    <-"..., 4096) = 4096
    read(8, "table(self, handle):\n           "..., 4096) = 4096
    read(8, "rrno using _sys_errlist (or siml"..., 4096) = 4096
    read(8, " p2cwrite = None, None\n         "..., 4096) = 4096
    write(2, "    ", 4)                     = 4
    write(2, "self.pid = os.fork()\n", 21)  = 21
    close(8)                                = 0
    munmap(0xb7d28000, 4096)                = 0
    write(2, "OSError", 7)                  = 7
    write(2, ": ", 2)                       = 2
    write(2, "[Errno 12] Cannot allocate memor"..., 33) = 33
    write(2, "\n", 1)                       = 1
    unlink("/var/run/sd-agent.pid")         = 0
    close(3)                                = 0
    munmap(0xb7e0d000, 4096)                = 0
    rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x589978}, {0xb89a60, [], SA_RESTORER, 0x589978}, 8) = 0
    brk(0xa022000)                          = 0xa022000
    exit_group(1)                           = ?
    
    7 回复  |  直到 9 年前
        1
  •  93
  •   vladr    10 年前

    作为一般规则(即在香草仁中), fork / clone 失败与 ENOMEM occur specifically 因为任何一个 失忆的情况 ( dup_mm , dup_task_struct , alloc_pid , mpol_dup , mm_init security_vm_enough_memory_mm 让你失望了 虽然 enforcing overcommit policy .

    首先检查fork尝试时未能fork的进程的vmsize,然后将其与overmit策略(插入数字)相关的可用内存量(物理和交换)进行比较

    在您的特定情况下,请注意,Virtuozzo additional checks overcommit enforcement . 此外,我不知道你到底有多大的控制权,从 在内部 你的集装箱,完毕 swap and overcommit configuration (以影响执行结果。)

    左边有两个选项 :

    • 切换到更大的实例,或者
    • 把一些编码工作放到 更有效地控制脚本的内存

    注意

    subprocess.Popen 使用 / 克隆 under the hood 再次请求与Python一样多的内存已经在消耗 ,也就是说,在几百个额外的MB中,都是为了那么 exec free ps .

    没有此父页的表等存在复制问题 vfork posix_spawn . 但如果你不想重写 子流程.Popen 依据 / posix\U产卵 ,考虑使用 suprocess.Popen 只有一次,在脚本开始时(当Python的内存占用最小时),要 生成一个shell脚本,然后运行 自由的 / ps公司 sleep 还有循环中的其他东西 与您的脚本并行;轮询脚本的输出或同步读取它,如果您有其他东西需要异步处理,则可以从一个单独的线程执行--使用Python进行数据处理,但将分叉留给下级进程。

    ps公司 自由的 在Python中,您可以直接从 procfs ,无论您选择自己访问还是通过 existing libraries and/or packages . 如果 ps公司 自由的 是你唯一运行的实用程序,那么你可以 废除 子流程.Popen .

    子流程.Popen 值得关注的是,如果你的脚本泄露了内存,你最终还是会碰壁的。注意一下,然后 check for memory leaks .

        2
  •  18
  •   Nima    11 年前

    看看 free -m

    您可以先检查是否启用了任何交换内存。

    $sudo swapon -s
    

    如果为空,则表示未启用任何交换。要添加1GB交换:

    $sudo dd if=/dev/zero of=/swapfile bs=1024 count=1024k
    $sudo mkswap /swapfile
    $sudo swapon /swapfile
    

    将以下行添加到 fstab

    $sudo vim /etc/fstab
    
         /swapfile       none    swap    sw      0       0 
    

    来源和更多信息可以找到 here

        3
  •  8
  •   pilcrow    16 年前

    交换可能不是先前建议的那样。问题中的python进程有多大 ENOMEM

    在内核2.6中, /proc/sys/vm/swappiness 控制内核转向交换的攻击性,以及 overcommit* 记录内核可以通过眨眼和点头来分配内存的数量和精确程度。比如你在facebook上的关系状况, it's complicated .

    …但是交换实际上是按需提供的(根据web主机)。。。

    但不是根据你的 free(1)

    适应 Red Hat KB Article 15252 :

    Red Hat Enterprise Linux 5系统 运行正常,没有交换空间 内存和systemv共享内存是 少于内存量的3/4。 [建议有] 最少 2GB交换空间。

    比较你的 /proc/sys/vm 普通CentOS 5.3安装的设置。添加交换文件。棘轮向下 swappiness

        4
  •  7
  •   serv-inc    7 年前

    echo 1 > /proc/sys/vm/overcommit_memory
    

    如果你确定你的系统有足够的内存。看到了吗 Linux over commit heuristic

        5
  •  5
  •   Jim Dennis    13 年前

    我仍然怀疑您的客户/用户加载了一些内核模块或驱动程序 正在干扰 clone() 系统调用(可能是一些模糊的安全增强, 有点像盖子,但更模糊?)或者正在填充一些内核数据 fork() 操作(工艺表,第页 表、文件描述符表等)。

    这是本书的相关部分 fork(2)

    ERRORS
           EAGAIN fork() cannot allocate sufficient memory to copy the parent's page tables and allocate a task  structure  for  the
                  child.
    
           EAGAIN It  was not possible to create a new process because the caller's RLIMIT_NPROC resource limit was encountered.  To
                  exceed this limit, the process must have either the CAP_SYS_ADMIN or the CAP_SYS_RESOURCE capability.
    
           ENOMEM fork() failed to allocate the necessary kernel structures because memory is tight.
    

    我建议让用户在引导到一个普通的内核之后尝试这个方法,并且只加载一组最小的模块和驱动程序(运行应用程序/脚本所需的最小数量)。从那里,假设它在该配置中工作,他们可以在该配置和显示问题的配置之间执行二进制搜索。这是标准的sysadmin疑难解答101。

    在你的 strace 是:

    clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0xb7f12708) = -1 ENOMEM (Cannot allocate memory)
    

    ... 我知道其他人也谈到了交换和内存可用性(我建议您至少设置一个小的交换分区,讽刺的是,即使它位于RAM磁盘上。。。当Linux内核有一点点交换可用时,通过它的代码路径比那些没有交换可用的(异常处理路径)要广泛得多。

    不过,我怀疑这仍然是一个红鲱鱼。

    事实上 free 自由的 输出。。。这里的应用程序问题可能是由某个专有内核模块引起的,它以某种方式干扰了内存分配。

    这不太可能是普通的Linux/UNIX问题。你有一些不标准的东西。

        6
  •  2
  •   totaam    14 年前

    您是否尝试过使用:

    (status,output) = commands.getstatusoutput("ps aux")
    

    我以为这已经解决了完全相同的问题。 但是我的进程最终被杀死而不是无法繁殖,更糟糕的是。。

    经过一些测试,我发现这只发生在较早版本的python上:它发生在2.6.5中,而不是2.7.2中

    我的搜索把我带到了这里 python-close_fds-issue 但是,令人不安的关闭的fds并没有解决这个问题。它仍然很值得一读。

    我发现python仅仅通过监视文件描述符就泄漏了文件描述符:

    watch "ls /proc/$PYTHONPID/fd | wc -l"
    

    和您一样,我确实希望捕获命令的输出,并且我确实希望避免OOM错误。。。但看起来唯一的办法是让人们使用一个没有那么多bug的Python版本。不太理想。。。

        7
  •  0
  •   codeDr    16 年前

    munmap(0xb7d280004096)=0
    写入(2,“OSError”,7)=7

    我见过类似这样的草率代码:

    serrno = errno;
    some_Syscall(...)
    if (serrno != errno)
    /* sound alarm: CATROSTOPHIC ERROR !!! */
    

    你应该检查一下,看看这是不是正在发生的事情 失败。

    编辑以添加:

    你不能说这个过程会持续多久。可能的内存消耗者

    • 分叉过程
    • 共享库
    • 内存映射文件
        8
  •  0
  •   GoingMyWay    6 年前

    $ sudo bash -c "echo vm.overcommit_memory=1 >> /etc/sysctl.conf"
    $ sudo sysctl -p
    

    参考文献: https://github.com/openai/gym/issues/110#issuecomment-220672405