¿Cómo matas a todos los procesos de Linux que tienen más de cierta edad?

Tengo un problema con algunos procesos tipo zombie en un determinado servidor que deben ser eliminados de vez en cuando. ¿Cómo puedo identificar mejor a los que se han ejecutado durante más de una hora más o menos?

Si solo necesitan ser asesinados:

 if [[ "$(uname)" = "Linux" ]];then killall --older-than 1h someprocessname;fi 

Si quieres ver lo que coincide

 if [[ "$(uname)" = "Linux" ]];then killall -i --older-than 1h someprocessname;fi 

La bandera -i indicará sí / no para cada coincidencia de proceso.

Encontré una respuesta que funciona para mí:

advertencia: esto encontrará y matará procesos de larga ejecución

 ps -eo uid,pid,etime | egrep '^ *user-id' | egrep ' ([0-9]+-)?([0-9]{2}:?){3}' | awk '{print $2}' | xargs -I{} kill {} 

(Donde ID de usuario es el ID de un usuario específico con procesos de larga ejecución).

La segunda expresión regular coincide con la hora que tiene una cifra de días opcional, seguida de una hora, minutos y un segundo componente, y por lo tanto tiene al menos una hora de duración.

Para cualquier cosa anterior a un día,

 ps aux 

le dará la respuesta, pero se reduce a la precisión del día que podría no ser tan útil.

 USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.0 7200 308 ? Ss Jun22 0:02 init [5] root 2 0.0 0.0 0 0 ? S Jun22 0:02 [migration/0] root 3 0.0 0.0 0 0 ? SN Jun22 0:18 [ksoftirqd/0] root 4 0.0 0.0 0 0 ? S Jun22 0:00 [watchdog/0] 

Si está en Linux u otro sistema con el sistema de archivos / proc, en este ejemplo, solo puede ver que el proceso 1 se ha estado ejecutando desde el 22 de junio, pero no hay indicación de la hora en que se inició.

 stat /proc/ 

le dará una respuesta más precisa. Por ejemplo, aquí hay una marca de tiempo exacta para el proceso 1, que ps muestra solo como Jun22:

 ohm ~$ stat /proc/1 File: `/proc/1' Size: 0 Blocks: 0 IO Block: 4096 directory Device: 3h/3d Inode: 65538 Links: 5 Access: (0555/dr-xr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2008-06-22 15:37:44.347627750 -0700 Modify: 2008-06-22 15:37:44.347627750 -0700 Change: 2008-06-22 15:37:44.347627750 -0700 

De esta manera puede obtener la lista de los diez procesos más antiguos:

  ps -elf |  ordenar -r -k12 |  cabeza -n 10 

Perl’s Proc :: ProcessTable hará el truco: http://search.cpan.org/dist/Proc-ProcessTable/

Puede instalarlo en debian o ubuntu con sudo apt-get install libproc-processtable-perl

Aquí hay un trazador de líneas:

 perl -MProc::ProcessTable -Mstrict -w -e 'my $anHourAgo = time-60*60; my $t = new Proc::ProcessTable;foreach my $p ( @{$t->table} ) { if ($p->start() < $anHourAgo) { print $p->pid, "\n" } }' 

O, más formateado, póngalo en un archivo llamado process.pl:

 #!/usr/bin/perl -w use strict; use Proc::ProcessTable; my $anHourAgo = time-60*60; my $t = new Proc::ProcessTable; foreach my $p ( @{$t->table} ) { if ($p->start() < $anHourAgo) { print $p->pid, "\n"; } } 

luego ejecuta perl process.pl

Esto le da más versatilidad y una resolución de 1 segundo en la hora de inicio.

Jodie C y otros han señalado que se puede usar killall -i , lo cual está bien si quieres usar el nombre del proceso para matar. Pero si quiere matar con los mismos parámetros que pgrep -f , debe usar algo como lo siguiente, usando pure bash y el sistema de archivos /proc .

 #!/bin/sh max_age=120 # (seconds) naughty="$(pgrep -f offlineimap)" if [[ -n "$naughty" ]]; then # naughty is running age_in_seconds=$(echo "$(date +%s) - $(stat -c %X /proc/$naughty)" | bc) if [[ "$age_in_seconds" -ge "$max_age" ]]; then # naughty is too old! kill -s 9 "$naughty" fi fi 

Esto le permite buscar y eliminar procesos anteriores a max_age segundos utilizando el nombre completo del proceso ; es decir, el proceso llamado /usr/bin/python2 offlineimap puede /usr/bin/python2 offlineimap por referencia a “offlineimap”, mientras que las soluciones de killall presentadas aquí solo funcionarán en la cadena “python2”.

Puede usar bc para unir los dos comandos en la respuesta de mob y obtener cuántos segundos transcurrieron desde que comenzó el proceso:

 echo `date +%s` - `stat -t /proc/ | awk '{print $14}'` | bc 

editar:

Fuera del aburrimiento mientras esperaban que se ejecutaran procesos largos, esto es lo que salió después de algunos minutos de tocar el violín:

 #file: sincetime #!/bin/bash init=`stat -t /proc/$1 | awk '{print $14}'` curr=`date +%s` seconds=`echo $curr - $init| bc` name=`cat /proc/$1/cmdline` echo $name $seconds 

Si pones esto en tu camino y lo llamas así: desde entonces

imprimirá el proceso cmdline y segundos desde que comenzó. También puedes poner esto en tu camino:

 #file: greptime #!/bin/bash pidlist=`ps ax | grep -i -E $1 | grep -v grep | awk '{print $1}' | grep -v PID | xargs echo` for pid in $pidlist; do sincetime $pid done 

Y que si corres:

 greptime  

donde los patrones son una cadena o expresión regular extendida, imprimirá todos los procesos que coincidan con este patrón y los segundos desde que comenzaron. 🙂

hacer una ps -aef . esto le mostrará la hora en que comenzó el proceso. Luego, usando el comando de date encuentre la hora actual. Calcule la diferencia entre los dos para encontrar la edad del proceso.

Hice algo similar a la respuesta aceptada, pero ligeramente diferente, ya que quiero hacer coincidir según el nombre del proceso y en función del mal proceso que se está ejecutando durante más de 100 segundos.

 kill $(ps -o pid,bsdtime -p $(pgrep bad_process) | awk '{ if ($RN > 1 && $2 > 100) { print $1; }}') 

stat -t /proc/ | awk '{print $14}'

para obtener la hora de inicio del proceso en segundos desde la época. Compare con la hora actual ( date +%s ) para obtener la edad actual del proceso.

El uso de ps es el camino correcto. Ya hice algo similar antes, pero no tengo la fuente a mano. En general, ps tiene una opción para indicarle qué campos mostrar y ordenar. Puede ordenar el resultado por tiempo de ejecución, grep el proceso que desea y luego matarlo.

HTH

En caso de que alguien necesite esto en C, puede usar readproc.h y libproc:

 #include  #include  float pid_age(pid_t pid) { proc_t proc_info; int seconds_since_boot = uptime(0,0); if (!get_proc_stats(pid, &proc_info)) { return 0.0; } // readproc.h comment lies about what proc_t.start_time is. It's // actually expressed in Hertz ticks since boot int seconds_since_1970 = time(NULL); int time_of_boot = seconds_since_1970 - seconds_since_boot; long t = seconds_since_boot - (unsigned long)(proc_info.start_time / Hertz); int delta = t; float days = ((float) delta / (float)(60*60*24)); return days; } 

Llegó a algún lado … aunque es simple y útil

Puede usar el comando en crontab directamente,

 * * * * * ps -lf | grep "user" | perl -ane '($h,$m,$s) = split /:/,$F +[13]; kill 9, $F[3] if ($h > 1);' 

o, podemos escribirlo como script de shell

 #!/bin/sh # longprockill.sh ps -lf | grep "user" | perl -ane '($h,$m,$s) = split /:/,$F[13]; kill + 9, $F[3] if ($h > 1);' 

Y llámalo crontab como tal,

 * * * * * longprockill.sh 

Mi versión de sincetime anterior por @Rafael S. Calsaverini:

 #!/bin/bash ps --no-headers -o etimes,args "$1" 

Esto invierte los campos de salida: tiempo transcurrido primero, comando completo incluyendo argumentos en segundo lugar. Esto es preferible porque el comando completo puede contener espacios.