Comment tuer tous les processus Linux plus anciens qu’un certain âge ?
Question
J'ai un problème avec certains processus de type zombie sur un certain serveur qui doivent être supprimés de temps en temps.Comment puis-je identifier au mieux ceux qui ont duré plus d’une heure environ ?
La solution
S'ils ont juste besoin d'être tués :
if [[ "$(uname)" = "Linux" ]];then killall --older-than 1h someprocessname;fi
Si tu veux voir à quoi ça correspond
if [[ "$(uname)" = "Linux" ]];then killall -i --older-than 1h someprocessname;fi
Le -i
flag vous demandera oui/non pour chaque correspondance de processus.
Autres conseils
J'ai trouvé une réponse qui fonctionne pour moi :
avertissement:cela trouvera et tuer processus de longue durée
ps -eo uid,pid,etime | egrep '^ *user-id' | egrep ' ([0-9]+-)?([0-9]{2}:?){3}' | awk '{print $2}' | xargs -I{} kill {}
(Où ID de l'utilisateur est l'ID d'un utilisateur spécifique avec des processus de longue durée.)
La deuxième expression régulière correspond à une heure comportant un chiffre de jours facultatif, suivi d'un composant heure, minute et seconde, et dure donc au moins une heure.
Pour tout ce qui date de plus d'un jour,
ps aux
vous donnera la réponse, mais elle se résume à une précision journalière, ce qui pourrait ne pas être aussi utile.
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 vous êtes sous Linux ou un autre système avec le système de fichiers /proc, dans cet exemple, vous pouvez voir uniquement que le processus 1 est en cours d'exécution depuis le 22 juin, mais aucune indication sur l'heure à laquelle il a été démarré.
stat /proc/<pid>
vous donnera une réponse plus précise.Par exemple, voici un horodatage exact pour le processus 1, que ps affiche uniquement sous la forme 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 cette façon, vous pouvez obtenir la liste des dix processus les plus anciens :
ps -elf | sort -r -k12 | head -n 10
Jodie C et d'autres ont souligné que killall -i
peut être utilisé, ce qui est très bien si vous souhaitez utiliser le nom du processus pour tuer.Mais si vous voulez tuer selon les mêmes paramètres que pgrep -f
, vous devez utiliser quelque chose comme ce qui suit, en utilisant pure bash et le /proc
système de fichiers.
#!/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
Cela vous permet de trouver et de tuer des processus plus anciens que max_age
secondes en utilisant le nom complet du processus;c'est-à-dire le processus nommé /usr/bin/python2 offlineimap
peut être tué en faisant référence à "offlineimap", alors que le killall
les solutions présentées ici ne fonctionneront que sur la chaîne « python2 ».
Proc::ProcessTable de Perl fera l'affaire :http://search.cpan.org/dist/Proc-ProcessTable/
Vous pouvez l'installer sur Debian ou Ubuntu avec sudo apt-get install libproc-processtable-perl
Voici un one-liner :
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" } }'
Ou, plus formaté, mettez ceci dans un fichier appelé 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";
}
}
puis cours perl process.pl
Cela vous donne plus de polyvalence et une résolution d'une seconde sur l'heure de démarrage.
Vous pouvez utiliser bc
pour joindre les deux commandes dans la réponse de mob et obtenir le nombre de secondes écoulées depuis le démarrage du processus :
echo `date +%s` - `stat -t /proc/<pid> | awk '{print $14}'` | bc
modifier:
Par ennui en attendant que de longs processus s'exécutent, voici ce qui est ressorti après quelques minutes de bidouillage :
#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 vous mettez ceci sur votre chemin et l'appelez comme ceci :depuis toujours
il imprimera la ligne de commande du processus et les secondes depuis le démarrage.Vous pouvez également mettre ceci sur votre chemin :
#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
Et que si vous exécutez :
greptime <pattern>
où patterns est une chaîne ou une expression régulière étendue, il affichera tous les processus correspondant à ce modèle et les secondes depuis leur démarrage.:)
fait une ps -aef
.cela vous montrera l'heure à laquelle le processus a commencé.Puis en utilisant le date
commande trouver l’heure actuelle.Calculez la différence entre les deux pour trouver l’âge du processus.
J'ai fait quelque chose de similaire à la réponse acceptée mais légèrement différemment puisque je souhaite faire une correspondance en fonction du nom du processus et en fonction du mauvais processus exécuté pendant plus de 100 secondes.
kill $(ps -o pid,bsdtime -p $(pgrep bad_process) | awk '{ if ($RN > 1 && $2 > 100) { print $1; }}')
stat -t /proc/<pid> | awk '{print $14}'
pour obtenir l'heure de début du processus en secondes depuis l'époque.Comparez avec l'heure actuelle (date +%s
) pour obtenir l'âge actuel du processus.
Utiliser ps est la bonne façon.J'ai déjà fait quelque chose de similaire auparavant, mais je n'ai pas la source sous la main.Généralement, ps a la possibilité de lui indiquer les champs à afficher et par lesquels trier.Vous pouvez trier la sortie par durée d'exécution, saisir le processus souhaité, puis le supprimer.
HTH
Si quelqu'un en a besoin en C, vous pouvez utiliser readproc.h et 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;
}
Je suis tombé quelque part... j'ai trouvé que c'était simple et utile
Vous pouvez utiliser la commande directement dans crontab,
* * * * * ps -lf | grep "user" | perl -ane '($h,$m,$s) = split /:/,$F
+[13]; kill 9, $F[3] if ($h > 1);'
ou, nous pouvons l'écrire sous forme de script shell,
#!/bin/sh
# longprockill.sh
ps -lf | grep "user" | perl -ane '($h,$m,$s) = split /:/,$F[13]; kill
+ 9, $F[3] if ($h > 1);'
Et appelle-le crontab comme ça,
* * * * * longprockill.sh
Ma version de sincetime
ci-dessus par @Rafael S.Calsaverini :
#!/bin/bash
ps --no-headers -o etimes,args "$1"
Cela inverse les champs de sortie :le temps écoulé en premier, la commande complète incluant les arguments en second.Ceci est préférable car la commande complète peut contenir des espaces.