Subprocesso Python.Popen “OSError: [Errno 12] Não é possível alocar memória”

114

Nota: Esta pergunta foi feita originalmente aqui, mas o tempo de recompensa expirou, embora uma resposta aceitável não tenha sido encontrada. Estou fazendo novamente esta pergunta, incluindo todos os detalhes fornecidos na pergunta original.

Um script Python está executando um conjunto de funções de classe a cada 60 segundos usando o módulo sched :

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

O script está sendo executado como um processo daemonizado usando o código aqui .

Vários métodos de classe que são chamados como parte de doChecks usam o módulo de subprocesso para chamar funções do sistema a fim de obter estatísticas do sistema:

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

Isso funciona bem por um período de tempo antes de todo o script travar com o seguinte erro:

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

A saída de free -m no servidor depois que o script travou é:

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

O servidor está executando CentOS 5.3. Não consigo reproduzir em minhas próprias caixas CentOS nem com nenhum outro usuário relatando o mesmo problema.

Tentei várias coisas para depurar isso, conforme sugerido na pergunta original:

  1. Registrando a saída de free -m antes e depois da chamada Popen. Não há mudança significativa no uso da memória, ou seja, a memória não está sendo gradualmente usada à medida que o script é executado.

  2. Eu adicionei close_fds = True para a chamada Popen, mas isso não fez diferença - o script ainda travava com o mesmo erro. Sugerido aqui e aqui .

  3. Eu verifiquei os rlimits que mostraram (-1, -1) em RLIMIT_DATA e RLIMIT_AS como sugerido aqui .

  4. Um artigo sugeriu que não ter espaço de troca pode ser a causa, mas a troca está realmente disponível sob demanda (de acordo com o host da web) e isso também foi sugerido como uma causa falsa aqui .

  5. Os processos estão sendo fechados porque esse é o comportamento de usar .communicate () conforme respaldado pelo código-fonte Python e comentários aqui .

As verificações inteiras podem ser encontradas no GitHub aqui com a função getProcesses definida na linha 442. Isso é chamado por doChecks () começando na linha 520.

O script foi executado com strace com a seguinte saída antes da falha:

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)                           = ?
davidmytton
fonte
1
runnig fora de 'pipes' ou filedescriptors ou um recurso de kernel relacionado a estes?
Blauohr,
Verifique /var/log/messagesou dmesgcomande.
mark4o
Não há nada no log relevante para isso.
davidmytton
Você já encontrou uma solução para isso? Tenho sintomas muito semelhantes. Eu tenho muita memória disponível, mas depois de adicionar swap (como algumas de suas respostas sugerem), o problema desaparece. Gostaria de saber se você descobriu alguma coisa nos meses entre então e agora. -- obrigado!
dpb
Estou tendo o mesmo problema, mas sem solução - alguma ideia?

Respostas:

88

Como regra geral (ou seja, baunilha kernels), fork/ clonefalhas com ENOMEM ocorrem especificamente por causa de qualquer um honesto a Deus fora-de-memória condição ( dup_mm, dup_task_struct, alloc_pid, mpol_dup, mm_initetc. coaxar), ou porque security_vm_enough_memory_mmvocê falhou enquanto reforçando a política overcommit .

Comece verificando o tamanho de vms do processo que falhou na bifurcação, no momento da tentativa de bifurcação e, em seguida, compare a quantidade de memória livre (física e de troca) no que se refere à política de overcommit (insira os números).

Em seu caso específico, observe que o Virtuozzo tem verificações adicionais na aplicação de overcommit . Além disso, não tenho certeza de quanto controle você realmente tem, de dentro do seu contêiner, sobre a configuração de troca e overcommit (para influenciar o resultado da aplicação).

Agora, para realmente seguir em frente, eu diria que você tem duas opções :

  • mudar para uma instância maior, ou
  • coloque algum esforço de codificação para controlar de forma mais eficaz a pegada de memória do seu script

NOTE que o esforço de codificação pode ser em vão se descobrir que não é você, mas algum outro cara colocado em uma instância diferente no mesmo servidor que você está executando o amock.

Em termos de memória, já sabemos que subprocess.Popenusa fork/ por clone baixo do capô , o que significa que toda vez que você o chama, você está solicitando mais uma vez a quantidade de memória que o Python já está consumindo , ou seja, centenas de MB adicionais, tudo para então execum pequeno executável de 10kB, como freeou ps. No caso de uma política de supercomprometimento desfavorável, você verá em breve ENOMEM.

Alternativas para forkque não tenham este problema de cópia de tabelas de páginas pai etc. são vforke posix_spawn. Mas se você não quiser reescrever pedaços de subprocess.Popenem termos de vfork/ posix_spawn, considere usar suprocess.Popenapenas uma vez, no início de seu script (quando a área de cobertura de memória do Python é mínima), para gerar um script de shell que então executa free/ ps/ sleepe tudo o mais em um loop paralelo ao seu script; sondar a saída do script ou lê-lo de forma síncrona, possivelmente a partir de um thread separado se você tiver outras coisas para cuidar de forma assíncrona - faça sua análise de dados em Python, mas deixe a bifurcação para o processo subordinado.

NO ENTANTO , no seu caso particular, você pode pular a invocação psefree ; essas informações estão prontamente disponíveis para você em Python diretamenteprocfs , quer você escolha acessá-las sozinho ou por meio de bibliotecas e / ou pacotes existentes . Se pse freefossem os únicos utilitários que você estava executando, você pode acabar com eles subprocess.Popencompletamente .

Finalmente, faça o que fizer no que diz subprocess.Popenrespeito a você, se o seu script vazar memória, você ainda vai bater na parede eventualmente. Fique de olho nele e verifique se há vazamentos de memória .

vladr
fonte
7
Descobri que a execução gc.collect()logo antes subprocess.Popenajuda nos casos em que o coletor de lixo não funcionou por um tempo.
letmaik 02 de
Eu escrevi um deamon para lidar com a estratégia do script auxiliar: github.com/SeanHayes/errand-boy Estou usando em produção com um de meus clientes e nossos problemas "Não é possível alocar memória" desapareceram.
Seán Hayes
Eu apreciaria um diagnóstico simples, por exemplo, seguir /proc/fd/mapspara determinar se a memória supercomprometida é de fato o problema
Dima Tisnek
18

Olhando para o resultado de free -m , parece-me que você realmente não tem memória swap disponível. Não tenho certeza se no Linux o swap sempre estará disponível automaticamente sob demanda, mas estava tendo o mesmo problema e nenhuma das respostas aqui realmente me ajudou. Adicionar um pouco de memória de swap, no entanto, corrigiu o problema no meu caso, portanto, como isso pode ajudar outras pessoas que enfrentam o mesmo problema, postarei minha resposta sobre como adicionar um swap de 1 GB (no Ubuntu 12.04, mas deve funcionar da mesma forma para outras distribuições).

Você pode primeiro verificar se há alguma memória swap habilitada.

$sudo swapon -s

se estiver vazio, significa que você não tem nenhuma troca habilitada. Para adicionar uma troca de 1 GB:

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

Adicione a seguinte linha ao fstabpara tornar a troca permanente.

$sudo vim /etc/fstab

     /swapfile       none    swap    sw      0       0 

Fonte e mais informações podem ser encontradas aqui .

Nima
fonte
1
Isso resolveu o mesmo problema ou algum outro?
Dima Tisnek
Isso funcionou para mim no CentOS 6.4. Ocorreu um erro ao instalar o awstats, obrigado.
Ruslan Abuzant,
Embora isso me permita executar o Código, não resolveu realmente o problema, que provavelmente está em uma biblioteca que uso.
philmaweb
1
Você consertou meu problema. Obrigado! +1
sscirrus
8

a troca pode não ser a pista falsa sugerida anteriormente. Qual é o tamanho do processo python em questão antes de ENOMEM?

No kernel 2.6, /proc/sys/vm/swappinesscontrola a agressividade com que o kernel se tornará a troca e overcommit*arquiva a quantidade e a precisão com que o kernel pode distribuir memória com um piscar de olhos e um aceno. Como seu status de relacionamento no Facebook, é complicado .

... mas a troca está disponível sob demanda (de acordo com o host) ...

mas não de acordo com a saída de seu free(1)comando, que mostra nenhum espaço de troca reconhecido por sua instância de servidor. Agora, seu host da web pode certamente saber muito mais do que eu sobre este tópico, mas os sistemas RHEL / CentOS virtuais que usei relataram troca disponível para o sistema operacional convidado.

Adaptando o Artigo 15252 do Red Hat KB :

Um sistema Red Hat Enterprise Linux 5 funcionará perfeitamente sem nenhum espaço de troca, desde que a soma da memória anônima e da memória compartilhada do sistema V seja menor que 3/4 da quantidade de RAM. .... Sistemas com 4 GB de RAM ou menos [é recomendado ter] um mínimo de 2 GB de espaço de troca.

Compare suas /proc/sys/vmconfigurações com uma instalação simples do CentOS 5.3. Adicione um arquivo de troca. Ratchet down swappinesse veja se você vive mais.

pilcrow
fonte
Qual é a melhor maneira de verificar o tamanho do processo Python? ps?
Davidmytton
algo como ps -o user,pid,vsz="Mem(Kb)" -o cmd $PYTHON_PID, ou top (1), deve fazê-lo.
Pilcrow
7

Para uma solução fácil, você poderia

echo 1 > /proc/sys/vm/overcommit_memory

se você tiver certeza de que seu sistema tem memória suficiente. Veja Linux sobre a heurística de commit .

serv-inc
fonte
1
Muito obrigado! Uma solução tão fácil, você salvou meu dia)
igolkotek
5

Continuo a suspeitar que seu cliente / usuário tem algum módulo de kernel ou driver carregado que está interferindo na clone()chamada do sistema (talvez algum aprimoramento de segurança obscuro, algo como LIDS, mas mais obscuro?) Ou está de alguma forma preenchendo algumas das estruturas de dados do kernel que são necessários para fork()/ clone()para operar (tabela de processos, tabelas de páginas, tabelas de descritores de arquivo, etc.).

Esta é a parte relevante da fork(2)página de manual:

ERRORS
       EAGAIN fork () não pode alocar memória suficiente para copiar as tabelas de página dos pais e alocar uma estrutura de tarefa para o
              criança.

       EAGAIN Não foi possível criar um novo processo porque o limite de recursos RLIMIT_NPROC do chamador foi encontrado. Para
              exceder esse limite, o processo deve ter o recurso CAP_SYS_ADMIN ou CAP_SYS_RESOURCE.

       ENOMEM fork () falhou ao alocar as estruturas de kernel necessárias porque a memória está apertada.

Eu sugiro que o usuário tente fazer isso depois de inicializar em um kernel genérico e com apenas um conjunto mínimo de módulos e drivers carregados (mínimo necessário para executar seu aplicativo / script). A partir daí, supondo que funcione naquela configuração, eles podem realizar uma pesquisa binária entre aquela e a configuração que exibe o problema. Esta é a solução de problemas 101 do administrador de sistemas padrão.

A linha relevante em seu straceé:

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

... Eu sei que outras pessoas falaram sobre swap e disponibilidade de memória (e eu recomendo que você configure pelo menos uma pequena partição de swap, ironicamente mesmo se estiver em um disco RAM ... os caminhos de código através do kernel do Linux quando tiver até mesmo uma pequena quantidade de swap disponível foi exercida muito mais extensivamente do que aqueles (caminhos de tratamento de exceção) em que não há swap disponível.

No entanto, suspeito que isso ainda seja uma pista falsa.

O fato de freeestar relatando memória 0 (ZERO) em uso pelo cache e buffers é muito perturbador. Suspeito que a freesaída ... e possivelmente o problema do seu aplicativo aqui, são causados ​​por algum módulo proprietário do kernel que está interferindo na alocação de memória de alguma forma.

De acordo com as páginas de manual de fork () / clone (), a chamada do sistema fork () deve retornar EAGAIN se sua chamada causar uma violação de limite de recursos (RLIMIT_NPROC) ... no entanto, não diz se EAGAIN deve ser retornado por outras violações RLIMIT *. Em qualquer caso, se o seu destino / host tiver algum tipo de Vormetric estranho ou outras configurações de segurança (ou mesmo se o seu processo estiver sendo executado sob alguma política SELinux estranha), isso pode estar causando essa falha -ENOMEM.

É muito improvável que seja um problema comum do Linux / UNIX. Você tem algo fora do padrão acontecendo aí.

Jim Dennis
fonte
1
O servidor está sendo executado em uma base de modelo de mídia (dv) que usa Virtuozzo para virtualização.
davidmytton
Tente pesquisar nos fóruns de mensagens do Virtuozzo e no sistema de rastreamento de bugs e, talvez, procurando atualizações para o próprio subsistema do Virtuozzo.
Jim Dennis
2

Você já tentou usar:

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

Achei que isso tinha resolvido exatamente o mesmo problema para mim. Mas então meu processo acabou sendo morto em vez de falhar na geração, o que é ainda pior.

Depois de alguns testes, descobri que isso ocorria apenas em versões mais antigas do python: isso acontece com 2.6.5, mas não com 2.7.2

Minha pesquisa me trouxe aqui python-close_fds-issue , mas desconfigurar closed_fds não resolveu o problema. Ainda vale a pena ler.

Descobri que o python estava vazando descritores de arquivo apenas por ficar de olho nele:

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

Como você, quero capturar a saída do comando e evitar erros de OOM ... mas parece que a única maneira é as pessoas usarem uma versão menos bugada do Python. Não é ideal...

totaam
fonte
0

munmap (0xb7d28000, 4096) = 0
gravação (2, "OSError", 7) = 7

Eu vi um código desleixado parecido com este:

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

Você deve verificar se é isso que está acontecendo no código python. Errno só é válido se a chamada de sistema em andamento falhar.

Editado para adicionar:

Você não diz quanto tempo dura esse processo. Possíveis consumidores de memória

  • processos bifurcados
  • estruturas de dados não utilizadas
  • bibliotecas compartilhadas
  • arquivos mapeados de memória
codeDr
fonte
2
Sim, mas vemos pelo strace do OP que a primeira falha syscall - de clone () - é ENOMEM conforme relatado. Esse erro é preservado ao longo do tropeço de pouca memória do python durante a construção do traceback, mesmo que a biblioteca C errnoseja redefinida várias vezes ao longo do caminho.
Pilcrow,