У меня проблема с некоторыми зомби-подобными процессами на определенном сервере, которые нужно время от времени уничтожать. Как я могу лучше всего определить те, которые работали более часа или около того?
Если их просто нужно убить
if [[ "$(uname)" = "Linux" ]];then killall --older-than 1h someprocessname;fi
Если вы хотите увидеть, что это соответствует
if [[ "$(uname)" = "Linux" ]];then killall -i --older-than 1h someprocessname;fi
-i
Флаг не подскажет вам да / нет для каждого матча процесса.
if [[ "$(uname)" = "Linux" ]];
? Разве соответствующая часть не является просто killall
командой? (Кажется, что окружающее if
предложение можно было бы удалить, чтобы сделать этот ответ немного более прямым)Использование PS это правильный путь. Я уже делал нечто подобное раньше, но у меня нет источника под рукой. Обычно - ps может указать, какие поля показывать и по каким сортировать. Вы можете отсортировать вывод по времени выполнения, выполнить grep требуемого процесса и затем убить его.
НТН
За все, что старше одного дня,
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, которая отображается только как июнь 22:
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
и stat
показывает разные результаты для меня. ps
показывает, что процесс начался 1 день назад, а статистика показывает, что началась сегодня. Почему? TIME
столбец в ps
выходных данных не показывает фактическое время выполнения процесса. Он показывает суммарное время ЦП процесса - время, в течение которого ЦП работали с процессом. ps -eo pid,etime | grep $PID
Нашел ответ, который работает для меня:
предупреждение: это найдет и убьет долго запущенные процессы
ps -eo uid,pid,etime | egrep '^ *user-id' | egrep ' ([0-9]+-)?([0-9]{2}:?){3}' | awk '{print $2}' | xargs -I{} kill {}
(Где user-id - это идентификатор конкретного пользователя с длительными процессами.)
Второе регулярное выражение соответствует времени с необязательным числом дней, за которым следуют час, минута и второй компонент, и поэтому его длина не менее одного часа.
etimes
вместо того, etime
чтобы всегда отображать прошедшее время в секундах, а не в днях / часах ...ps
v3.2.8 из Debian Squeeze не поддерживает etimes
параметр, однако v3.3.3 из Debian Wheezy делает. Таким образом, вы можете получить список десяти самых старых процессов:
пс-эльф | сортировка -r -k12 | голова -n 10
сделать ps -aef
. это покажет вам время, когда процесс начался. Затем с помощью date
команды найдите текущее время. Рассчитайте разницу между ними, чтобы найти возраст процесса.
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; }}')
stat -t /proc/<pid> | awk '{print $14}'
чтобы получить время начала процесса в секундах с начала эпохи. Сравните с текущим временем ( date +%s
), чтобы узнать текущий возраст процесса.
stat -t /proc/<pid> | awk '{print $14}'
- date +%s
| bc»В случае, если кому-то это нужно в 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
он напечатает cmdline процесса и секунды с момента его запуска. Вы также можете поставить это на своем пути:
#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>
где узоры - это строка или расширенное регулярное выражение, он распечатает все процессы, соответствующие этому шаблону, и секунды, прошедшие с момента их запуска. :)
Джоди Си и другие отметили, что 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».
Наткнулся где-то .. думал, это просто и полезно
Вы можете использовать команду в 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
Моя версия sincetime
выше @Rafael S. Calsaverini:
#!/bin/bash
ps --no-headers -o etimes,args "$1"
Это инвертирует поля вывода: сначала прошедшее время, полная команда, включая аргументы, вторую. Это предпочтительно, потому что полная команда может содержать пробелы.
killall -i --older-than 1h someprocessname
pgrep
и, следовательно, является более гибким, чемkillall
.