Cerrar descriptores de fichero abiertos en tiempo de ejecución en GNU/Linux
Cuando una aplicación tiene muchos ficheros abiertos y hace un uso intenso de CPU puede ser útil cerrar determinados ficheros o directorios con los que este trabajando sin tener que matar el proceso. Por ejemplo, si logrotate tuviera un directorio con varios millones de ficheros logs, este consumiría una tasa muy elevada de recursos, entre ellos un tiempo de espera de entrada y salida elevado.
Lo interesante en este caso sería quitarle trabajo cerrando algunos o todos los descriptores de ficheros abiertos con los que trabaja. Veamos como sería el proceso basándonos en dicho supuesto con logrotate. Para cerrar ficheros abiertos de un determinado proceso utilizaremos gdb.
Consumo de procesos.
Tasks: 130 total, 2 running, 127 sleeping, 0 stopped, 1 zombie Cpu(s): 9.9%us, 9.0%sy, 3.7%ni, 83.3%id, 0.6%wa, 0.0%hi, 0.1%si, 0.0%st Mem: 32880924k total, 31808928k used, 1071996k free, 1091368k buffers Swap: 0k total, 0k used, 0k free, 7597524k cached PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 7227 root 30 10 2335m 2.2g 1048 R 250.1 16.9 2410:41 logrotate 32596 root 20 0 124g 19g 4.4g S 2.0 62.2 43131:35 java 32643 root 20 0 4359m 1.0g 7336 S 2.0 3.2 1515:56 java 1 root 20 0 19232 992 708 S 0.0 0.0 3:10.78 init ...
El problema de CPU con logrotate viene del número de ficheros de uno de sus directorios configurados para rotar logs.
find /var/log/logstash/ | wc -l 3596509
Limpiar fichero de estado de logrotate.
wc -l /var/lib/logrotate.status 3159814 /var/lib/logrotate.status du -m /var/lib/logrotate.status 598 /var/lib/logrotate.status grep -ci logstash /var/lib/logrotate.status 2153799 # Eliminamos las lineas que contengan el patrón logstash. sed -i '/logstash/d' /var/lib/logrotate.status grep -ci logstash /var/lib/logrotate.status 0 du -m /var/lib/logrotate.status 1 /var/lib/logrotate.status
Listar los descriptores de ficheros para el proceso logrotate 7227 (Número 3)
ls -lt /proc/7227/fd total 0 lr-x------ 1 root root 64 Sep 17 14:33 0 -> /dev/null l-wx------ 1 root root 64 Sep 17 14:33 1 -> /dev/null l-wx------ 1 root root 64 Sep 17 14:33 2 -> /dev/null lr-x------ 1 root root 64 Sep 17 14:33 3 -> /var/log/logstash
Cerrar un descriptor de fichero abierto con la llamada al sistema close(), para el ejemplo el número 3.
echo -e "call close(3)\nquit" > gdb_commands gdb -p 7227 --batch -x gdb_commands
NOTA: Por supuesto también se puede hacer desde la terminal de gdb.
Comprobamos que el uso de recursos a mermado.
Cpu(s): 7.2%us, 0.3%sy, 0.0%ni, 72.3%id, 0.0%wa, 0.0%hi, 0.1%si, 0.0%st