Script para controlar la Memoria Cache en GNU/Linux

Recientemente en GUTL hemos estado comentando algunos de los problemas relativos a la gestión de la memoria cache en Linux, particularmente en Debian. En algunas ocasiones mi portátil llegaba al punto del colapso, no tenía más remedio que liberar la memoria cache manualmente, y en casos extremos: reiniciaba el sistema. Esto es debido a que cuando tenemos muchas aplicaciones abiertas, aunque cerremos una buena parte de ellas, estas quedan en la memoria ram (cache). La próxima vez que las abramos tardarán mucho menos en ejecutarse.

 

Memoria caché:

Área de almacenamiento dedicada a los datos usados o solicitados con más frecuencia para su recuperación a gran velocidad.

Sin embargo, lo que debería ser una ventaja, en ocasiones supone un problema. En máquinas de bajos recursos cuando necesitamos iniciar otras aplicaciones, es probable que no dispongan de la suficiente memoria ram para ejecutarse de manera fluida. Para ello es necesario liberar la memoria cache ejecutando el siguiente comando: sync; echo 3 > /proc/sys/vm/drop_caches.

Hay varias formas de gestionar esta tarea mediante la creación de scripts. Aquí en el portal GUTL hay buena información al respecto: liberando memoria ram desde la terminal o consola y como limpiar los caches de memoria en gnulinux.

Personalmente suelo utilizar un script muy pequeño para facilitar esta tarea. En resumidas cuentas:

Contenido del archivo: limpiar-cache.sh

#!/bin/sh

sync && echo 3 > /proc/sys/vm/drop_caches 

permisos de ejecución y carpeta destino:

sudo chmod +x limpiar-cache.sh

sudo cp limpiar-cache.sh /usr/local/bin

Invocando: limpiar-cache.sh desde la consola mitigaba el problema. También supe hacer uso del comando crontab para prescindir de la terminal, y que el script se ejecutara cada cierto tiempo. El problema es que en muchos casos limpiaba la cache cuando no lo necesitaba, y en otras ocasiones, estaba expectante a que crontab hiciera su trabajo. Esta idea no me daba el resultado que yo deseaba, volví al método manual. Cabe aclarar que ahora sería factible su uso.

Finalmente —a raíz de un comentario de Pablo (@elMor3no)— me decidí a crear un script automatizado muy sencillo, que tenga parámetros preestablecidos, y en función de estos, realice la tarea sin mi intervención. Se trata de un bucle infinito, pero no hay de que preocuparse, no consume nada. Básicamente el script sera gestionado por systemd. Aunque no soy un gran admirador de este, admito que tiene sus cosas buenas: como lo práctico.

El script

El script con permisos de ejecución está alojado en la ruta /root/scripts/, no obstante, podría estar alojado en otra ruta, como: /usr/local/bin.

sudo chmod +x control-cache.sh
sudo mkdir /root/scripts
sudo cp control-cache.sh /root/scripts
Todas las tareas las realicé como usuario root. Tanto los nombres de los archivos: script, service, como los de las variables en dicho script, son de ejemplos.

Archivo: /root/scripts/control-cache.sh

#!/bin/sh

# Control de la Memoria Cache

memoria_minima=300
tiempo_espera=15

while true; do

free_memory=`free -m | grep Mem | awk '{print $4}'`

    if [ $free_memory -le $memoria_minima ]; then

        sync; echo 3 > /proc/sys/vm/drop_caches

    fi

    sleep $tiempo_espera

done

Descargar control-cache.sh

Una breve descripción de su función

memoria_minima: Aquí se define el piso mínimo (umbral) en donde queremos que comience a liberase memoria cache. En este caso está fijado en 300MB de memoria ram disponible.

tiempo_espera: Este valor es importante, y establece el tiempo que tardará en volver a ejecutarse el bucle (while) finalizada la última sentencia. Es tarea del usuario establecer un valor razonable.

free_memory: Esta variable guarda el estado actual de la memoria ram disponible, dicho valor será comparado con la memoria mínima asignada por el usuario (umbral). El tiempo entre cada comparación está determinado por la sentencia: sleep $tiempo_espera

Obtención del valor free_memory utilizando grep y awk para filtrar:

En la próxima linea empleo una condicional (if): «Si la memoria (free) disponible es menor o igual que el valor mínimo asignado por el usuario» ejecutar la próxima sentencia: sync; echo 3 > /proc/sys/vm/drop_caches. Seguidamente, se haya cumplido o no la condición, espera el tiempo establecido por el usuario, y vuelve a ejecutarse el ciclo. De acuerdo con estos valores, cuando la memoria ram disponible toque el piso de los 300MB: libera la cache, espera 15 segundos, y vuelve a iniciar el ciclo de comprobación.

El tiempo de espera (sleep) es particularmente importante porque de otra forma aumentaría los ciclos de la cpu. Además, cuando perfore el piso mínimo —300MB en este caso—, de mantenerse por un tiempo, por ej.: en 285MB – 290MB, se estará ejecutando: sync; echo 3 > /proc/sys/vm/drop_caches hasta leer un valor superior a 300MB, en el cual se detendrá. En este hipotético caso se estaría liberando memoria cada 15 segundos, por lo tanto, un tiempo razonable sería de 30 a 60 segundos, aunque con 15 seg. se desempeñó bastante bien. Depende de cada uno establecer ese valor, al igual que el piso mínimo de la memoria ram.

Archivo de unidad (systemd)

Llegó el momento de gestionar el script control-cache.sh como un servicio. Con privilegios root edité el archivo de unidad control-cache.service en el directorio /etc/systemd/system/. A este archivo no hay que asignarle permisos de ejecución.

Contenido del archivo: /etc/systemd/system/control-cache.service

[Unit]
Description=Control de la Memoria Cache

[Service]
Type=simple
ExecStart=/root/scripts/control-cache.sh
WorkingDirectory=/root/scripts
Restart=on-failure
StandardOutput=syslog
StandardError=syslog

[Install]
WantedBy=multi-user.target

Descargar control-cache.service

 

No hay mucho que decir, es un archivo de unidad bastante simple de entender:

Type=simple: es el servicio por defecto, debe iniciarse inmediatamente, además el proceso no debe romperse. No utilizar este tipo si otros servicios tienen que ser llamados por ese servicio, a menos que no sea activado por el socket.

ExecStart=: como su nombre lo indica, es el encargado de ejecutar el script en la ruta establecida.

WorkingDirectory=: directorio de trabajo, generalmente el mismo donde se aloja la aplicación (script).

Restart=on-failure=: reinicia el servicio en caso de fallar.

StandardOutput=: salida estándar de los registros (log), si no se especifica nada, por defecto escribirá en el archivo /var/log/syslog. En caso de especificarse null, redirige los registros a /dev/null.

StandardError=: ídem al anterior para casos de error.

WantedBy=multi-user.target=: es el análogo al nivel 3 en SystemV, y permite habilitar el script para que se inicie en el arranque del sistema.

En caso de modificar el archivo control-cache.service es necesario ejecutar: sudo systemctl daemon-reload para que se apliquen los cambios.

Habilitar el archivo de servicio

Una vez que tengamos ambos archivos en sus respectivas rutas: /root/scripts/control-cache.sh y /etc/systemd/system/control-cache.service, es momento de manipular el servicio mediante los siguientes comandos:

Arranque, parada y reinicio:

sudo systemctl start control-cache.service

sudo systemctl stop control-cache.service

sudo systemctl restart control-cache.service

verificar que haya iniciado correctamente:

sudo systemctl status control-cache.service

Si todo ha ido bien, podemos habilitarlo en el arranque:

sudo systemctl enable control-cache.service

La siguiente imagen muestra el servicio en estado habilitado en el inicio del sistema y activo:

También es posible controlarlo con los siguientes comandos:

sudo service control-cache start | stop | status

Testear el script

Para asegurarte de que el script se adapta a tus exigencias, o simplemente quieres saber como se comporta, puedes realizar algunas pruebas de estrés de la siguiente forma: en 3 terminales, o una sola terminal dividida en 3 ventanas (tmux, terminator), en lo posible elige la opción «Siempre encima» para que no pierdan el foco. En cada una escribir el siguiente comando:

  • Terminal 1: watch free -m
  • Terminal 2: tail -f /var/log/syslog
  • Terminal 3: htop

Antes de abrir aplicaciones, chequear en htop (terminal 3) que los ciclos de cpu están bien. A continuación observando la memoria libre disponible (terminal 1), comienza a abrir aplicaciones hasta aproximarse al umbral que escogiste, supongamos: 300MB, e intenta perforar ese piso por unos pocos megas —digamos 280, 295—. El tiempo de respuesta estará dado entre: 1 seg. y el tiempo de espera que seleccionaste (ej.: 15 seg.). Observa el registro de syslog (terminal 2) y comprueba que el script está respondiendo.

Luego prueba mantener aplicaciones abiertas, a tal punto, que no pueda superar el techo de los 300MB. Observa el registro syslog y verifica que el tiempo de espera para limpiar la memoria cache, en repeticiones consecutivas, es el que se ajusta a tus necesidades. No descuidar Htop.

En caso de escribir el script, prestar especial atención en la lineas de código. Hay casos en los que el script funciona, pero, la cpu aumenta los ciclos considerablemente.

Consideraciones finales

También es posible automatizar esta tarea mediante crontab —aunque personalmente, a modo de gestionar el script, prefiero systemd—. Por ejemplo, realizar una tarea cada 30 segundos todos los días: 30 * * * * /root/scripts/control-cache.sh. En este caso el script solo debe contar con la linea de comprobación, el ciclo lo realiza crontab:

#!/bin/sh

# Control de la Memoria Cache

memoria_minima=300

free_memory=`free -m | grep Mem | awk '{print $4}'`

    if [ $free_memory -le $memoria_minima ]; then

        sync; echo 3 > /proc/sys/vm/drop_caches

    fi

El script no hace magia, y lamentablemente tampoco gestiona la ram, eso es tarea del sistema. control-cache.sh simplemente intenta otorgarle independencia al usuario, para que no tenga que estar todo el tiempo expectante. Espero que a alguien le sea utilidad.


Fuente:

https://wiki.archlinux.org/index.php/Systemd_(Español)#Tipos_de_servicios

¿Te resultó interesante? Compártelo ...



Percaff_TI99

Publicado por Percaff_TI99

http://gutl.jovenclub.cu/ » Forma parte de GUTL desde el 10 agosto, 2013. Amante de la ciencia y tecnología en general. Usuario de GNU/Linux desde hace varios años.

Este artículo tiene 14 comentarios

  1. Magistral clase de dominio de nuestro sistema. Te aseguro que la «Trilobite» (mi laptop personal) te lo va a agradecer (aunque lo que tenga sea Lubuntu con LXQt y no el Debian que mencionas como ejemplo).

    Ojala podamos retomar este tipo de publicaciones mas seguidamente en nuestro portal. Mil gracias por el aporte… voy a probarlo y te digo

  2. A mi personalmente me da error a la hora de activarlo como servicio:

    neji@neji-laptop:~$ sudo systemctl status control-cache.service
    ● control-cache.service
    Loaded: loaded (/etc/systemd/system/control-cache.service; enabled; vend
    Active: failed (Result: exit-code) since Tue 2019-12-17 20:53:44 CST; 14
    Process: 2644 ExecStart=/usr/local/bin/limpiar-cache.sh (code=exited, sta
    Main PID: 2644 (code=exited, status=203/EXEC)

    dic 17 20:53:44 neji-laptop systemd[1]: control-cache.service: Service Rest
    dic 17 20:53:44 neji-laptop systemd[1]: control-cache.service: Scheduled re
    dic 17 20:53:44 neji-laptop systemd[1]: Stopped control-cache.service.
    dic 17 20:53:44 neji-laptop systemd[1]: control-cache.service: Start reques
    dic 17 20:53:44 neji-laptop systemd[1]: control-cache.service: Failed with
    dic 17 20:53:44 neji-laptop systemd[1]: Failed to start control-cache.serv

    A lo mejor es porque lo puse en /usr/local/bin y no en /root/scripts como tu dices??

    • Hola @Neji:

      En el archivo de systemd estás usando ExecStart=/usr/local/bin/limpiar-cache.sh. Corrobora que en /usr/local/bin se llame limpiar-cache.sh y que tenga permisos de ejecución. Ese error generalmente se debe a que no concuerdan los nombres.

      En caso de que figure /usr/local/bin/control-cache.sh, edita a: ExecStart=/usr/local/bin/contol-cache.sh. Luego un sudo systemctl daemon-reload.

      El script que automatiza la tarea es control-cache.sh. No se si le cambiaste el nombre en ‘/usr/local/bin’ o utilizaste el ejemplo: limpiar-cache.sh, el cuál describí más arriba y no es el que controla la memoria.

      Saludos.

      • Llamé limpiar-cache.sh al archivo que realiza la tarea y control-cache.service al servicio.

        limpiar-cache.sh tiene los permisos de ejecución dados, systemd me lo da como disponible pero no lo activa.

        • Descargué ambos archivos de aquí (GUTL), renombré y le di permisos a limpiar-cache.sh, lo copié en /usr/local/bin/ y funciona.


          ● control-cache.service - Control de la Memoria Cache
          Loaded: loaded (/etc/systemd/system/control-cache.service; disabled; vendor preset: enabled)
          Active: active (running) since Thu 2019-12-19 12:17:36 -03; 4s ago
          Main PID: 4023 (limpiar-cache.s)
          Tasks: 2 (limit: 2255)
          Memory: 1000.0K
          CGroup: /system.slice/control-cache.service
          ├─4023 /bin/sh /usr/local/bin/limpiar-cache.sh
          └─4033 sleep 15

          dic 19 12:17:36 debian systemd[1]: Started Control de la Memoria Cache.

          Lo que debe estar sucediendo es que no editaste la ruta WorkingDirectory a /usr/local/bin en control-cache.service


          [Service]
          Type=simple
          ExecStart=/usr/local/bin/limpiar-cache.sh
          WorkingDirectory=/usr/local/bin
          Restart=on-failure
          StandardOutput=syslog
          StandardError=syslog

          • Yo cree el script mio a partir del tuyo….

            #!bin/sh

            # Controlar la Cache de forma Automatica
            # Script basado en el original de Percaff_TI99 (GUTL)

            minimo=143
            espera=15

            while true; do
            #Analizando cuanta memoria libre queda
            memoria_libre=`free -m | grep Mem | awk '{print $4}'`

            #Si la memoria libre es menor o igual que el minimo limpio cache
            if [ $memoria_libre -le $minimo ]; then
            sync; echo 3 > /proc/sys/vm/drop_caches
            fi
            sleep $espera
            done

            Edite el servicio como mismo tu dices
            [unit]
            Description=Controla la Memoria Cache de Linux

            [Service]
            Type=simple
            ExecStart=/usr/local/bin/limpiar-cache.sh
            WorkingDirectory=/usr/local/bin
            Restart=on-failure
            StandardOutput=syslog
            StandardError=syslog

            [Install]
            WantedBy=multi-user.target

            Luego puse disponible el servicio pero da problemas a la hora de activarlo…. he hecho como 15 veces lo de systemctl daemon-reload pero sigue sin activarse el script.

          • El script que hiciste funciona, el problema está en la primera linea: #!bin/sh por #!/bin/sh.

            Además en el archivo de servicio [unit] por [Unit]. Sino te va a salir el error /etc/systemd/system/control-cache.service:1: Unknown section ‘unit’. Aunque igual funciona.

            Cuando hagas los cambios en ambos archivos, dale a: sudo systemctl daemon-reload, sudo systemctl start control-cache.service.

            Si estaba activo: sudo systemctl restart control-cache.service

            No deberías tener problemas, más que nada, era ese pequeño detalle en el script.

          • Mil gracias… he corregido el script y el servicio y afortunadamente ya puedo ver en mi consola algo como esto:

            neji@neji-laptop:~$ sudo systemctl status control-cache.service
            [sudo] password for neji:
            ● control-cache.service - Controla la Memoria Cache de Linux
            Loaded: loaded (/etc/systemd/system/control-cache.service; enabled;
            Active: active (running) since Fri 2019-12-20 20:37:11 CST; 7min ag
            Main PID: 14909 (limpiar-cache.s)
            CGroup: /system.slice/control-cache.service
            ├─14909 /bin/sh /usr/local/bin/limpiar-cache.sh
            └─15680 sleep 15

            dic 20 20:37:11 neji-laptop systemd[1]: Started Controla la Memoria Ca
            dic 20 20:37:11 neji-laptop systemd[14909]: Failed to attach 14909 to

          • Me alegro @Neji. Veo que figura un error: «Failed to attach 14909», pero seguramente cuando reinicie la portátil no debería estar. Yo lo probé copiando/pegando tu script (con los cambios mencionados) y funciona correctamente, sin ningún tipo de advertencia.

            Saludos.

  3. Hahhha yo pense que era el unico loco que tenia su propio script :V hahaha, solo que el mio se meten rm en los /var/log , /tmp …

    • En este script se pueden evitar mandándolos al /dev/null, aunque creo que probé y seguían apareciendo en syslog. Desde una versión en adelante de systemd funciona.

      Borrar todos los /var/logs es como que te deja en ascuas si tienes algún problema con el sistema.

      Saludos.

Los comentarios están cerrados.