Как убить все процессы Linux старше определенного возраста?

У меня проблема с некоторыми зомби-подобными процессами на определенном сервере, которые время от времени нужно убивать. Как мне лучше всего определить те, которые работали дольше часа или около того?

Ответов (14)

Решение

Если их просто нужно убить:

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

Если вы хотите увидеть, что это соответствует

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

-i Флаг не подскажет вам да / нет для каждого матча процесса.

Таким образом вы можете получить список из десяти самых старых процессов:

ps -elf | sort -r -k12 | голова -n 10

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

чтобы получить время начала процесса в секундах с начала эпохи. Сравните с current time ( date +%s ), чтобы получить текущий возраст процесса.

Если кому-то это нужно на C, вы можете использовать readproc.h и 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;
}

Вы можете использовать bc для объединения двух команд в ответе моба и узнать, сколько секунд прошло с момента запуска процесса:

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

редактировать:

От скуки в ожидании запуска долгих процессов вот что получилось после нескольких минут возни:

#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

Если вы поместите это на свой путь и назовете это так: Sincetime

он напечатает командную строку процесса и секунды с момента запуска. Вы также можете указать это на своем пути:

#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

И чем, если вы запустите:

greptime <pattern>

где шаблоны - это строка или расширенное регулярное выражение, он распечатает все процессы, соответствующие этому шаблону, и секунды с момента их запуска. :)

Где-то наткнулся .. думал просто и полезно

Вы можете использовать команду напрямую в crontab,

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

или мы можем написать его как сценарий оболочки,

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

И назовите это так, crontab,

* * * * * longprockill.sh

Джоди Си и другие указали, что killall -i это можно использовать, и это нормально, если вы хотите использовать имя процесса для уничтожения. Но если вы хотите убить с теми же параметрами, что и pgrep -f, вам нужно использовать что-то вроде следующего, используя чистый bash и /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     

Это позволяет вам находить и уничтожать процессы старше max_age секунд, используя полное имя процесса ; т.е. названный процесс /usr/bin/python2 offlineimap может быть остановлен ссылкой на «offlineimap», тогда как killall представленные здесь решения будут работать только со строкой «python2».

Моя версия sincetime выше от @Rafael S. Calsaverini:

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

Это меняет местами поля вывода: сначала истекшее время, затем полная команда, включая аргументы. Это предпочтительно, потому что полная команда может содержать пробелы.

Для всего, что старше одного дня,

ps aux

даст вам ответ, но он упадет до дневной точности, что может быть не так полезно.

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]

Если вы используете Linux или другую систему с файловой системой / proc, в этом примере вы можете видеть только, что процесс 1 запущен с 22 июня, но не указывает время его запуска.

stat /proc/<pid>

даст вам более точный ответ. Например, вот точная отметка времени для процесса 1, который ps показывает только как 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

Использование ps - правильный путь. Я уже делал нечто подобное раньше, но у меня нет исходного кода. Обычно - ps имеет возможность указать, какие поля отображать и по каким сортировать. Вы можете отсортировать вывод по времени выполнения, выполнить поиск нужного процесса и затем убить его.

HTH

Нашел ответ, который мне подходит:

предупреждение: это найдет и убьет долго работающие процессы

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

(Где user-id - это идентификатор конкретного пользователя с длительными процессами.)

Второе регулярное выражение соответствует времени, которое имеет необязательное число дней, за которым следуют час, минута и второй компонент, поэтому его длина составляет не менее одного часа.

do a ps -aef . this will show you the time at which the process started. Then using the date command find the current time. Calculate the difference between the two to find the age of the process.

Perl Proc :: ProcessTable сделает свое дело: http://search.cpan.org/dist/Proc-ProcessTable/

Вы можете установить его в debian или ubuntu с помощью sudo apt-get install libproc-processtable-perl

Вот однострочный:

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" } }'

Или, в более точном формате, поместите это в файл с именем 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";
    }
}

затем беги perl process.pl

Это дает вам большую гибкость и разрешение в 1 секунду при запуске.

Я сделал что-то похожее на принятый ответ, но немного по-другому, так как я хочу сопоставить на основе имени процесса и на основе плохого процесса, работающего более 100 секунд.

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