Me he encontrado con un problema extraño en el que un ps -o args -p <pid>
comando muy ocasionalmente no puede encontrar el proceso en cuestión, aunque definitivamente se está ejecutando en el servidor en cuestión. Los procesos en cuestión son scripts de reinicio de larga duración utilizados para iniciar algunas aplicaciones Java.
Las ocurrencias "en estado salvaje" de la cuestión siempre parecen ocurrir temprano en la mañana, por lo que hay cierta evidencia de que se ha relacionado con la carga de disco en el servidor en cuestión, porque están muy fuertemente cargados entonces, pero ejecutando el ps
en pregunta en un bucle cerrado, eventualmente puedo replicar el problema: una vez cada cientos de ejecuciones obtengo un error.
Al ejecutar el siguiente script bash, he logrado generar resultados extraños tanto para una ejecución fallida como exitosa:
while [ $? == 0 ] ; do strace -o fail.out ps -o args -p <pid> >/dev/null ; done ; strace -o good.out ps -o args -p <pid>
Comparando el resultado de fail.out
y good.out
, puedo ver que la getdents
llamada del sistema en la ejecución que falla de alguna manera devuelve un número mucho menor que el recuento real de procesos en el sistema (del orden de ~ 500 en comparación con ~ 1100)
grep getdents good.out
getdents(5, /* 1174 entries */, 32768) = 32760
getdents(5, /* 31 entries */, 32768) = 992
getdents(5, /* 0 entries */, 32768) = 0
grep getdents fail.out
getdents(5, /* 673 entries */, 32768) = 16728
getdents(5, /* 0 entries */, 32768) = 0
... y esa lista más corta no incluye el pid real en cuestión, por lo que no se encuentra.
Puede ignorar esta sección, los errores ENOTTY se explican por el comentario de dave_thompson a continuación, y no están relacionados
Además, la ejecución fallida obtiene algunos
ENOTTY
errores que no aparecen en la ejecución exitosa. Cerca del comienzo de la salida veoioctl (1, TIOCGWINSZ, 0x7fffe19db310) = -1 ENOTTY (ioctl inapropiado para dispositivo) ioctl (1, TCGETS, 0x7fffe19db280) = -1 ENOTTY (ioctl inapropiado para dispositivo)
Y al final veo un solo
ioctl (1, TCGETS, 0x7fffe19db0d0) = -1 ENOTTY (ioctl inapropiado para el dispositivo)
El error
ioctl
al final ocurre justo antes de lasps
devoluciones, pero ocurre después de queps
ya ha impreso un conjunto de resultados vacío, por lo que no estoy seguro de si están relacionados. Sé que son consistentes en todos los resultados de strace fallidos que tengo, pero no aparecen en los exitosos.
No tengo absolutamente ninguna idea de por getdents
qué ocasionalmente no encontraría la lista completa de procesos, y ahora he llegado al punto en el que solo voy a aplicar una curita en todo el asunto cambiando el script de control que verifica el script de envoltura en cuestión para llamar por ps
segunda vez si falla la primera, pero me interesaría saber si alguien tiene alguna idea de lo que está sucediendo aquí.
El sistema en cuestión ejecuta Kernel 4.16.13-1.el7.elrepo.x86_64 en CentOS 7 y procps-ng versión 3.3.10-17.el7_5.2.x86_64
>/dev/null
en la invocación 'falla' (en el bucle) pero no la invocación 'buena', de ahí la ENOTTY en fd 1.Respuestas:
Considere leer la información que necesita directamente del
/proc
sistema de archivos en lugar de a través de una herramienta comops
. Encontrará la información que busca ("args") dentro del archivo/proc/$pid/cmdline
, solo separada por bytes NUL en lugar de espacios.Puede usar esta
sed
línea para obtener los argumentos del proceso$pid
:Este comando es equivalente a:
(El uso de
args=
inps
omitirá el encabezado).El
sed
comando primero buscará el último byte NUL final y lo reemplazará con una nueva línea, y luego reemplazará todos los demás bytes NUL (separando argumentos individuales) con espacios, finalmente produciendo el mismo formato que está viendops
.Con respecto a los procesos de listado en el sistema, lo
ps
hace enumerando directorios/proc
, pero hay condiciones de carrera inherentes a ese procedimiento, ya que los procesos comienzan y salen mientrasps
se ejecuta, por lo que lo que obtienes no es realmente una instantánea sino una aproximación. En particular, es posible queps
muestre procesos que ya han finalizado para el momento en que muestra sus resultados, u omita procesos que se iniciaron mientras se estaba ejecutando (pero que el núcleo no devolvió al enumerar el contenido de/proc
).Siempre supuse que si un proceso está allí antes de
ps
comenzar y sigue ahí después de que se haya hecho, entonces no se lo perderá, supuse que el núcleo garantizaría que siempre se incluirían, incluso si hay una gran cantidad de otros procesos siendo creado y destruido. Lo que estás describiendo implica que ese no es el caso. Todavía estoy escéptico sobre eso, pero dado que hay condiciones de carrera conocidas en cuanto a cómops
funciona, supongo que es al menos plausible que la lista de PIDs/proc
pueda perder una existente debido a esas condiciones de carrera.Sería posible verificar eso comprobando la fuente del kernel de Linux, pero no lo he hecho (todavía), así que realmente no puedo decir con certeza si existe una condición de carrera que fallaría un proceso de larga ejecución, como tú describes.
La otra parte es la forma en que
ps
funciona. Incluso si le está pasando un PID único con el-p
argumento, sigue enumerando todos los PID existentes, aunque solo esté interesado en ese único. Definitivamente, podría tomar un atajo en ese caso y omitir la lista de las entradas/proc
e ir directamente a/proc/$pid
.No puedo decir por qué se implementó de esta manera. Quizás porque la mayoría de las
ps
opciones son "filtros" en los procesos, por lo que implementarlo de-p
la misma manera fue más fácil, tomar un atajo para ir directamente/proc/$pid
podría involucrar una ruta de código separada o duplicación de código ... Otra hipótesis es que algunos casos que incluyen-p
más opciones adicionales terminan requiriendo una lista, por lo que quizás sea complejo determinar qué casos exactos permitirían tomar el acceso directo y cuáles no.Lo que nos lleva a la solución alternativa, yendo directamente
/proc/$pid
, sin enumerar el conjunto completo de PID del sistema, evitando todas las razas conocidas y simplemente obteniendo la información que necesita directamente de la fuente.Es un poco feo, pero el problema que describe realmente existe, debería ser una forma confiable de obtener esa información.
fuente