Desfragmentación de la falla de RAM / OOM

11

Esta pregunta es bastante larga, así que haré las preguntas en la parte superior y luego pasaré por mi método para llegar a las preguntas:

  1. ¿No se ejecutó rm (basado en Busybox) porque no había suficiente RAM contigua?
  2. Si es así, ¿existe un método ligero para desfragmentar el DMA, sin recurrir a un reinicio del sistema?
  3. Si no, ¿qué lo causó? ¿Cómo puedo evitar que suceda en el futuro?

Después de que nuestro sistema de prueba se había ejecutado con bastante intensidad en los últimos días, ingresé por telnet al sistema y verifiqué los resultados de la prueba. Cuando vine a eliminar algunos datos, el sistema devolvió la línea de comando (como si el comando se hubiera ejecutado correctamente). Cuando llegué a revisar el directorio para obtener otro conjunto de resultados, vi que el archivo todavía existía (usando ls).

Después de esto, noté que más y más de mis comandos de shell no funcionaban como se esperaba.

Comenzaré con una salida de dmesg después de que rm no se ejecute correctamente:

La asignación de la longitud 61440 del proceso 6821 (rm) falló

DMA por CPU:

CPU 0: hola: 0, btch: 1 usd: 0

Active_anon: 0 active_file: 1 inactive_anon: 0 inactive_file: 0 unevictable: 6 dirty: 0 writeback: 0 instable: 0 free: 821 slab: 353 mapped: 0 pagetables: 0 bounce: 0

DMA libre: 3284kB min: 360kB bajo: 448kB alto: 540kB active_anon: 0kB inactive_anon: 0kB active_file: 4kB inactive_file: 0kB unevictable: 24kB presente: 8128kB pages_scanned: 0 all_unreclaimable? No

lowmem_reserve []: 0 0 0

DMA: 31 * 4kB 47 * 8kB 42 * 16kB 64 * 32kB 1 * 64kB 0 * 128kB 0 * 256kB 0 * 512kB 0 * 1024kB 0 * 2048kB 0 * 4096kB = 3284kB

14 páginas de caché total de páginas

No se puede asignar RAM para procesar datos, errno 12

Inicialmente, pensé que no podía ejecutar el programa en la mayor parte de la memoria contigua. Lo que significa que el DMA estaba demasiado fragmentado y tendría que encontrar una manera de hacer que el sistema desfragmente la memoria.

Luego hice una rápida comprobación matemática / cordura y me di cuenta de que el programa debería haber podido ejecutarse en la única ranura de memoria contigua de 64kB. Rm estaba solicitando 61440 bytes (60kB).

Hice una buena "desfragmentación manual" y reinicié el sistema. Cuando reinicié el sistema, saqué / proc / buddyinfo:

Node 0, zone DMA 2 8 3 12 0 1 0 1 0 1 0

El cual sospecho que es un mapa:

  • 2 x 4 kB
  • 8 x 8 kB
  • 3 x 16 kB
  • 12 x 32 kB
  • 1 x 128 kB
  • 1 x 512 kB

Pero si se suma la lista de valores anterior, no coincide con la salida de / proc / meminfo :

MemTotal:           6580 kB
MemFree:            3164 kB
Buffers:               0 kB
Cached:              728 kB
SwapCached:            0 kB
Active:              176 kB
Inactive:            524 kB
Active(anon):          0 kB
Inactive(anon):        0 kB
Active(file):        176 kB
Inactive(file):      524 kB`
Unevictable:           0 kB
Mlocked:               0 kB
MmapCopy:            844 kB
SwapTotal:             0 kB
SwapFree:              0 kB
Dirty:                 0 kB
Writeback:             0 kB
AnonPages:             0 kB
Mapped:                0 kB
Slab:               1268 kB
SReclaimable:        196 kB
SUnreclaim:         1072 kB
PageTables:            0 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:        3288 kB
Committed_AS:          0 kB
VmallocTotal:          0 kB
VmallocUsed:           0 kB
VmallocChunk:          0 kB

En resumen, mis preguntas son:

  1. ¿No se ejecutó rm porque no había suficiente RAM contigua?
  2. Si es así, ¿existe un método ligero para desfragmentar el DMA, sin recurrir a un reinicio del sistema?
  3. Si no, ¿qué lo causó? ¿Cómo puedo evitar que suceda en el futuro?

Estoy usando XPort Pro de Lantronix (8 MB, sistema operativo Linux) con uClinux versión 2.6.30. La cáscara en uso es silencio.

OldTinfoil
fuente
Punto menor: omitió 1 x 2048 kB de su lista de fragmentos de memoria. Si incluye eso, entonces la suma es 3192 kB, que está muy cerca de los 3164 kB enumerados en / proc / meminfo.
Alex Selby el

Respuestas:

11

En su pregunta 2 (desfragmentación de memoria), citando https://www.kernel.org/doc/Documentation/sysctl/vm.txt :

memoria_compacta

Disponible solo cuando CONFIG_COMPACTION está configurado. Cuando se escribe 1 en el archivo, todas las zonas se compactan de modo que la memoria libre esté disponible en bloques contiguos siempre que sea posible. Esto puede ser importante, por ejemplo, en la asignación de páginas enormes, aunque los procesos también compactarán directamente la memoria según sea necesario.

esto implica que el siguiente comando (ejecutado con privilegios de root y si la opción del kernel mencionada anteriormente estaba habilitada)

echo 1 > /proc/sys/vm/compact_memory

debería decirle al kernel que intente desfragmentar la memoria tanto como sea posible. Tenga en cuenta que, por ejemplo, en algunas versiones de RHEL6, esto puede bloquear el núcleo ...

Andre Holzner
fuente
1
¡Gracias por pasar el tiempo para volver y comentar una vieja pregunta!
OldTinfoil
7

Me tomó un poco de tiempo, pero pensé que esperaría para contestar hasta que tuviera respuestas a mis 3 subpreguntas.

Sin embargo, antes de comenzar, mencionaré que el término correcto cuando se trata de "fragmentar" la memoria de trabajo se refiere a "compactar" la memoria de trabajo.

1. ¿No se ejecutó rm porque no había suficiente RAM contigua?

Estuve correcto en mi conclusión: rm no se ejecutó porque no había suficiente RAM contigua. El sistema había estado adquiriendo RAM y fragmentándolo, haciéndolo irrecuperable.

2. Si es así, ¿existe un método ligero para desfragmentar el DMA sin recurrir a un reinicio del sistema?

Resulta que no hay forma de compactar la memoria, salvo reiniciar el sistema incorporado. En el caso de un sistema sin MMU, la prevención es el nombre del juego.

Parte de mí reflexiona si es posible hackear el kernel de Linux para emular la MMU en el software. Me imagino que si fuera posible, alguien ya lo habría hecho. No puedo imaginar que sea un concepto completamente nuevo;)

3. ¿Cómo puedo evitar que suceda en el futuro?

Para este proyecto, estaba usando cron para iniciar manualmente el programa cada vez que se requería. Una forma mucho mejor de hacer esto es llamar al programa al inicio y luego forzar el programa a que se suspenda hasta que sea necesario. De esta manera, no es necesario asignar memoria a cada uso. Reduciendo así la fragmentación.

En la primera iteración del proyecto, confiamos en mis llamadas de script de shell para realizar funciones críticas (como rm). No vimos la necesidad de reinventar la rueda si no fuera necesario.

Sin embargo, recomendaría evitar el shell donde sea posible para un sistema sin MMU:

( Pregunta , ¿qué pasa si ejecutas ls -la /path/to/directory/ | grep file-i-seek?)

( Respuesta : comienza un nuevo subproceso)

Si necesita implementar algunas de las funciones del script de shell central en su programa C, le recomiendo que consulte el código fuente utilizado en BusyBox . Lo más probable es que use C en su sistema integrado.

OldTinfoil
fuente
Gracias por tomarse el tiempo de regresar y compartir sus hallazgos.
Caleb
3
[Me doy cuenta de que esto es viejo] Emular una MMU es difícil ... Sin una MMU, cada programa usa directamente las direcciones físicas tal como aparecen en el bus de memoria. Podrías emular uno, pero tendrías que interceptar cada acceso a la memoria (como lo hace un MMU real). El rendimiento sería terrible. Alternativamente, puede usar punteros indirectos (como lo hizo Mac OS Classic, llamándolos "manejadores"), pero luego tiene una API completamente difícil y muy difícil frente a la preferencia (Mac OS Classic usó multitarea cooperativa) .
derobert
Gracias por regresar y dedicar tiempo a escribir esa respuesta. No sabía que MacOS classic hizo eso.
OldTinfoil