===== Cerrar descriptores de fichero abiertos en tiempo de ejecución en GNU/Linux =====
Cuando una aplicación tiene muchos [[https://www.busindre.com/configurar_limite_de_ficheros_abiertos|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