2008-08-08 13 views
65

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?¿Cómo se eliminan todos los procesos de Linux que son anteriores a cierta edad?

+5

En Linux use 'killall -i --older-than 1h someprocessname' – sanmai

+0

O vea mi respuesta que usa' pgrep' y por lo tanto es más flexible que 'killall'. – g33kz0r

Respuesta

28

Si sólo necesitan ser matado:

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

Si quieres ver lo que es a juego

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

La bandera -i le indicará si/no para cada partido proceso.

+0

Buen consejo. Me encontré con el --derder-- que cambiar más tarde, pero el -i lo hace útil para comprobar antes de matar quién sabe qué. – yukondude

+3

¿Cuál es el punto de 'if [[" $ (uname) "=" Linux "]];'? ¿La porción relevante no es solo el comando 'killall'? (Parece que la cláusula 'if' de los alrededores podría eliminarse para hacer que esta respuesta sea un poco más directa) – rinogo

+0

@ringo Porque en algunos sistemas (por ejemplo, Solaris), killall es un comando completamente diferente.En Solaris, finaliza todos los comandos. – ctc

0

El uso de ps es la manera correcta. Ya hice algo similar antes, pero no tengo la fuente a mano. Generalmente, 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

22

Para cualquier cosa más viejo que un día,

ps aux 

le dará la respuesta, pero desciende a día precisión 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ás en Linux u otro sistema con el sistema de archivos/proc, en este ejemplo, sólo se puede ver que el proceso 1 ha estado funcionando desde el 22 de junio, pero que se inició ninguna indicación de la hora.

stat /proc/<pid> 

le dará una respuesta más precisa. Por ejemplo, aquí hay un sello de tiempo exacto para el proceso 1, que ps muestra sólo 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 
+0

Parece que 'ps' y' stat' muestran resultados diferentes para mí. 'ps' muestra que el proceso comenzó hace 1 día y la estadística muestra que comenzó hoy. ¿Por qué? – pl1nk

+0

Tenga en cuenta que la columna 'TIME' en la salida' ps' no muestra el tiempo de ejecución real de un proceso. Muestra el tiempo de CPU acumulado del proceso, el tiempo que las CPU funcionaron con el proceso. –

+0

gracias, stat/proc/ me dio el resultado exacto para la vida del proceso (tiempo de inicio) – baptx

36

encontrado 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 user-id es ID de un usuario específico con procesos de larga duración.)

La segunda expresión regular coincide con la hora a 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.

+5

Umm, ¿estás matando el proceso? Espero que las personas se den cuenta de que este código no solo busca, sino que también mata, o pueden enojarse. –

+0

@ButtleButkus Buen punto. Sí, todo el motivo de la pregunta era encontrar los procesos anteriores y matarlos, pero no se menciona explícitamente todo eso con claridad. Nota para los demás: ignore la última parte de la línea a menos que disfrute las llamadas molestas de los usuarios. – yukondude

+2

wtf! por favor cambie el título de la pregunta. ¡afortunadamente no tuve el proceso! –

9

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

ps -elf | sort -r -k12 | head -n 10
+0

cosas buenas, gracias por esto. –

+0

En realidad, eso le da los 10 procesos más recientes porque de manera predeterminada muestra STIME, que es el momento en que se inició el programa. Si mostraba ETIME cuál es el tiempo transcurrido desde que se inició el programa, entonces esto sería correcto. –

2

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

+0

Lamentablemente, la salida de tiempo aquí es difícil de analizar. Puede ser "HH: MM" para procesos de ejecución corta, o "MonDD" (¡posiblemente localizado!) O incluso solo el año para procesos muy largos. –

7

Proc de Perl :: 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 una sola línea:

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, ponlo en un archivo llamado proceso.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"; 
    } 
} 

continuación, ejecute perl process.pl

Esto le da más versatilidad y 1-segunda resolución sobre la hora de inicio.

1

hice algo similar a la respuesta aceptada, pero un poco diferente ya que quiero para que coincida con base en nombre del proceso y en base a la mala proceso que se ejecuta durante más de 100 segundos

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

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

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

+0

Podemos unir los dos comandos para obtener segundos desde que comenzó el proceso: "echo' stat -t/proc/ | awk '{print $ 14}' '-' fecha +% s' | bc " –

+1

Esto no siempre tenga razón, al menos no para los sistemas Linux 2.6. Tengo un proceso que comenzó a las 9:49 pero stat -t (y stat) muestran que comenzó a las 13:14. –

+0

@dpk: a veces tienes un proceso principal y algunas horquillas ejecutándose. El proceso principal debe ser 9:49, pero el proceso hijo puede tener un tiempo más reciente. Lo mismo se aplica a los hilos de un proceso. – higuita

0

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

#include <proc/readproc.h> 
#include <proc/sysinfo.h> 

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; 
} 
3

Puede utilizar bc a unirse a los dos comandos en la respuesta de la multitud y obtener el número de segundos ellapsed desde que se inició el proceso:

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

edición:

Por aburrimiento a la espera de largos procesos para ejecutar , esto es lo que salió después de unos minutos jugueteando:

#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 su camino y lo llama así: desde la vez

imprimirá el proceso cmdline y segundos desde que se inició. También se puede poner esto en su 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 se ejecuta:

greptime <pattern> 

donde los patrones es una cadena o expresión regular extendida, se imprimirá a cabo todos los procesos coincidan con este patrón y el segundos desde que comenzaron. :)

7

Jodie C y otros han señalado que se puede usar killall -i, lo cual está bien si quiere 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 encontrar y matar procesos mayores de max_age segundos utilizando el nombre proceso completo; es decir, el proceso llamado /usr/bin/python2 offlineimap se puede eliminar por referencia a "offlineimap", mientras que las soluciones killall presentadas aquí solo funcionarán en la cadena "python2".

0

Encontré somewhere..thought es simple y útil

Usted puede usar el comando crontab en forma directa,

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

o, podemos escribir 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 
Cuestiones relacionadas