usage python linux memory

usage - Python subprocess.Popen "OSError:[Errno 12] No se puede asignar memoria"



python memory usage (6)

munmap (0xb7d28000, 4096) = 0
write (2, "OSError", 7) = 7

He visto un código descuidado que se ve así:

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

Debería verificar si esto es lo que está sucediendo en el código python. Errno solo es válido si la llamada al sistema en curso falló.

Editado para agregar:

No dices cuánto dura este proceso. Posibles consumidores de memoria

  • procesos bifurcados
  • estructuras de datos no utilizados
  • bibliotecas compartidas
  • archivos de memoria mapeados

Nota: esta pregunta se hizo originalmente here pero el tiempo de recompensa expiró a pesar de que no se encontró realmente una respuesta aceptable. Estoy volviendo a hacer esta pregunta, incluidos todos los detalles proporcionados en la pregunta original.

Una secuencia de comandos python ejecuta un conjunto de funciones de clase cada 60 segundos utilizando el módulo sched :

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

La secuencia de comandos se ejecuta como un proceso demoniaco utilizando el código here .

Una cantidad de métodos de clase que se llaman como parte de doChecks usan el módulo de subprocess para llamar a las funciones del sistema a fin de obtener estadísticas del sistema:

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

Esto funciona bien durante un período de tiempo antes de que todo el script se bloquee con el siguiente error:

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

El resultado de free -m en el servidor una vez que se ha bloqueado el script es:

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

El servidor ejecuta CentOS 5.3. No puedo reproducir en mis propios cuadros de CentOS ni con ningún otro usuario que informe el mismo problema.

He intentado varias cosas para depurar esto como se sugirió en la pregunta original:

  1. Registrando la salida de free -m antes y después de la llamada de Popen. No hay cambios significativos en el uso de la memoria, es decir, la memoria no se está agotando gradualmente a medida que se ejecuta el script.

  2. Añadí close_fds = True a la llamada de Popen, pero esto no hizo ninguna diferencia: el script aún se bloqueó con el mismo error. Sugerido here y here .

  3. Verifiqué los límites que mostraban (-1, -1) tanto en RLIMIT_DATA como en RLIMIT_AS como se sugiere here .

  4. Un artículo sugirió que no tener espacio de intercambio podría ser la causa, pero el intercambio está realmente disponible a pedido (según el servidor web) y esto también se sugirió como una causa falsa here .

  5. Los procesos se están cerrando porque ese es el comportamiento de usar .communicate () como respaldado por el código fuente de Python y los comentarios here .

Las comprobaciones completas se pueden encontrar en GitHub aquí con la función getProcesses definida desde la línea 442. DoChecks () la llama desde la línea 520.

La secuencia de comandos se ejecutó con precisión con la siguiente salida antes del bloqueo:

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) = ?


¿Has intentado usar:

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

Pensé que esto me había solucionado exactamente el mismo problema. Pero luego mi proceso terminó siendo asesinado en lugar de no generar, lo que es aún peor ...

Después de algunas pruebas descubrí que esto solo ocurría en versiones anteriores de python: sucede con 2.6.5 pero no con 2.7.2

Mi búsqueda me había llevado aquí python-close_fds-issue , pero al cerrar closed_fds no había resuelto el problema. Todavía vale la pena leerlo.

Descubrí que Python filtraba descripciones de archivos simplemente manteniéndolo vigilado:

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

Al igual que usted, quiero capturar el resultado del comando, y quiero evitar los errores OOM ... pero parece que la única manera es que la gente use una versión menos defectuosa de Python. No es ideal...


Al mirar el resultado de free -m me parece que en realidad no tiene memoria de intercambio disponible. No estoy seguro si en Linux el intercambio siempre estará disponible automáticamente a pedido, pero estaba teniendo el mismo problema y ninguna de las respuestas aquí realmente me ayudó. Sin embargo, al agregar memoria de intercambio, solucioné el problema en mi caso, ya que esto podría ayudar a otras personas que enfrentan el mismo problema, publico mi respuesta sobre cómo agregar un intercambio de 1GB (en Ubuntu 12.04 pero debería funcionar de manera similar para otras distribuciones).

Primero puede verificar si hay alguna memoria de intercambio activada.

$sudo swapon -s

si está vacío, significa que no tiene habilitado ningún intercambio. Para agregar un intercambio de 1GB:

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

Agregue la siguiente línea al fstab para que el intercambio sea permanente.

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

Fuente y más información se pueden encontrar here .


Como regla general (es decir, en núcleos de vainilla), las fallas de fork / clone con ENOMEM ocurren específicamente debido a una condición de dup_mm memoria honesta a Dios ( dup_mm , dup_task_struct , alloc_pid , mpol_dup , mm_init , etc. mm_init ), o porque security_vm_enough_memory_mm le falló mientras enforcing la política de compromiso excesivo .

Comience por verificar el vmsize del proceso que no se bifurcó, en el momento del intento de la horquilla, y luego compare la cantidad de memoria libre (física y de intercambio) en lo que se refiere a la política de sobrecompromiso (tape los números).

En su caso particular, tenga en cuenta que Virtuozzo tiene controles adicionales en la aplicación de sobrecompromiso . Además, no estoy seguro de cuánto control tiene realmente, desde su contenedor, sobre la configuración de intercambio y sobrecomprado (con el fin de influir en el resultado de la ejecución).

Ahora, para seguir adelante, diría que te quedan dos opciones :

  • cambiar a una instancia más grande, o
  • pon un poco de esfuerzo de codificación para controlar de forma más efectiva la huella de memoria de tu script

TEN EN CUENTA que el esfuerzo de codificación puede ser en vano si resulta que no eres tú, sino otro tipo colocado en una instancia diferente en el mismo servidor que ejecutaste.

Desde el punto de vista de la memoria, ya conocemos ese subprocess.Popen usa fork / clone debajo del capó , lo que significa que cada vez que lo llames estás solicitando una vez más la memoria que Python ya está consumiendo , es decir, en los cientos de MB adicionales. todo para ejecutar un pequeño ejecutable de 10kB como free o ps . En el caso de una política de compromiso excesivo desfavorable, pronto verá ENOMEM .

Las alternativas a la fork que no tienen este problema de copia de tablas de páginas padre, etc. son vfork y posix_spawn . Pero si no tiene ganas de volver a escribir trozos de subprocess.Popen en términos de vfork / posix_spawn , considere usar suprocess.Popen solo una vez, al comienzo de su script (cuando la huella de memoria de Python es mínima), para generar un script de shell que luego ejecuta free / ps / sleep y cualquier otra cosa en un bucle paralelo a su secuencia de comandos; sondea el resultado del guión o lo lee de forma síncrona, posiblemente desde un hilo separado si tienes otras cosas que ocuparte de forma asíncrona: haz tu crujido de datos en Python pero deja el bifurcación al proceso subordinado.

SIN EMBARGO , en su caso particular, puede omitir invocar ps y free completo; esa información está disponible para ti en Python directamente desde procfs , ya sea que elijas acceder a ella tú mismo o mediante bibliotecas y / o paquetes existentes . Si ps y free fueran las únicas utilidades que estaba ejecutando, entonces puede eliminar el subprocess.Popen . subprocess.Popen completamente .

Finalmente, haga lo que haga en lo que respecta al subprocess.Popen se refiere, si su script pierde la memoria, eventualmente golpeará la pared. Vigílalo y verifica si hay pérdidas de memoria .


Sigo sospechando que su cliente / usuario tiene algún módulo kernel o controlador cargado que está interfiriendo con la llamada al sistema clone() (tal vez alguna oscura mejora de seguridad, algo así como LIDS pero más oscuro?) O de alguna manera está llenando algo del kernel estructuras de datos que son necesarias para el funcionamiento de fork() / clone() (tabla de procesos, tablas de páginas, tablas de descriptores de archivos, etc.).

Aquí está la parte relevante de la página man de 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.

Sugiero que el usuario intente esto después de iniciar en un kernel genérico de valores y con solo un conjunto mínimo de módulos y controladores cargados (el mínimo necesario para ejecutar su aplicación / script). A partir de ahí, suponiendo que funcione en esa configuración, pueden realizar una búsqueda binaria entre eso y la configuración que muestra el problema. Esta es la solución de problemas estándar de sysadmin 101.

La línea relevante en tu strace es:

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

... Sé que otros han hablado sobre la disponibilidad de intercambio y memoria (y recomendaría que configure al menos una pequeña partición de intercambio, irónicamente, incluso si está en un disco RAM ... el código corre a través del kernel de Linux cuando tiene incluso un poco de intercambio disponible se ha ejercido mucho más extensivamente que aquellos (rutas de manejo de excepciones) en las que no hay intercambio disponible.

Sin embargo, sospecho que esto sigue siendo una pista falsa.

El hecho de que la memoria free 0 (CERO) en uso por la memoria caché y los almacenamientos intermedios es muy preocupante. Sospecho que el resultado free ... y posiblemente el problema de su aplicación aquí, son causados ​​por algún módulo kernel propietario que de alguna manera está interfiriendo con la asignación de memoria.

De acuerdo con las páginas man para fork () / clone (), la llamada al sistema fork () debería devolver EAGAIN si su llamada causaría una violación de límite de recursos (RLIMIT_NPROC) ... sin embargo, no dice si EAGAIN debe ser devuelto por otras violaciones RLIMIT *. En cualquier caso, si su destino / host tiene algún tipo de extraño Vormetric u otra configuración de seguridad (o incluso si su proceso se está ejecutando bajo alguna extraña política de SELinux), entonces podría estar causando este error -ENOMEM.

Es bastante improbable que sea un problema normal de Linux / UNIX. Tienes algo no estándar pasando allí.


el intercambio puede no ser el arenque sugerido previamente. ¿Qué tan grande es el proceso de Python en cuestión justo antes del ENOMEM ?

Bajo kernel 2.6, /proc/sys/vm/swappiness controla qué tan agresivamente el kernel cambiará para intercambiar, y overcommit* archivos de cuánto y con qué precisión el kernel puede distribuir la memoria con un guiño y un guiño. Al igual que el estado de su relación de Facebook, es complicado .

... pero el intercambio está realmente disponible a pedido (según el servidor web) ...

pero no de acuerdo con el resultado de su comando free(1) , que no muestra ningún espacio de intercambio reconocido por su instancia de servidor. Ahora, su proveedor de alojamiento web seguramente sabrá mucho más que yo acerca de este tema, pero los sistemas virtuales RHEL / CentOS que he utilizado han informado de intercambio disponible para el sistema operativo invitado.

Adaptación del artículo 15252 de Red Hat KB :

Un sistema Red Hat Enterprise Linux 5 funcionará perfectamente sin espacio de intercambio, siempre que la suma de la memoria anónima y la memoria compartida del sistema V sea inferior a aproximadamente 3/4 de la cantidad de RAM. ... Sistemas con 4 GB de ram o menos [se recomienda tener] un mínimo de 2 GB de espacio de intercambio.

Compare su configuración /proc/sys/vm con una instalación simple de CentOS 5.3. Agregue un archivo de intercambio. Trinquete la swappiness y vea si vive por más tiempo.