您如何杀死所有超过特定年龄的Linux进程?


68

我在某个服务器上需要不时地杀死某些类似于僵尸进程的问题。我如何最好地识别运行时间超过一个小时左右的设备?


5
在Linux中使用killall -i --older-than 1h someprocessname
sanmai 2012年

或者查看我的答案,该答案使用pgrep,因此比killall
g33kz0r

Answers:


32

如果只需要杀死他们:

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

如果要查看它的匹配项

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

-i标志将为每个过程匹配提示您是/否。


好提示。后来我偶然发现了--old-than开关,但是-i使它在杀死谁知道什么之前进行检查很有用。
育空地区2012年

3
有什么意义if [[ "$(uname)" = "Linux" ]];?相关部分不只是killall命令吗?(似乎if可以删除周围的子句以使此答案更直接一些)
rinogo 2014年

1
@ringo因为在某些系统上(例如Solaris),killall是一个完全不同的命令。在Solaris上,它将终止所有命令。
ctc

2
请注意,使用killall的'--regex'选项会使'--old-than'被忽略。喜悦!
杰里米

36

找到了一个对我有用的答案:

警告:这将发现并杀死长时间运行的进程

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

(其中user-id是具有长时间运行进程的特定用户的ID。)

第二个正则表达式匹配一个具有可选天数的时间,后跟一个小时,分钟和第二个部分,因此长度至少为一个小时。


5
嗯,你在杀死这个过程吗?我希望人们意识到此代码不仅会找到,而且会杀死人,否则可能会感到不高兴。
Buttle Butkus 2012年

@ButtleButkus好点。是的,问题的全部原因是找到旧进程并杀死它们,但是并没有明确提到所有这些。给其他人的提示:除非您喜欢来自用户的愤怒呼叫,否则请忽略最后一行。
yukondude 2012年

2
wtf!请更改问题的标题。幸运的是我没有这个过程!
neal aise 2012年

3
您可以使用optionetimes而不是etime始终以秒为单位显示经过的时间,而不是以天/小时为单位...
Marki555 2014年

1
@yukondude你是对的。我刚刚检查了它,ps来自debian squeeze的v3.2.8不支持该etimes参数,但是来自debian wheezy的v3.3.3支持。
Marki555 2014年

22

对于任何一天以上的事情,

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自6月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

看来,psstat正显示出我的不同的结果。ps表示该过程是1天前开始的,而stat显示是今天开始的。为什么?
pl1nk 2012年

1
请注意,输出中的TIMEps未显示进程的实际运行时间。它显示了该进程的累计CPU时间-CPU对该进程进行工作的时间。
matthias krull 2012年

谢谢,stat / proc / <pid>为我提供了进程生存期(启动时间)的准确结果
baptx

1
我还要顺便指出,如果该流程是在上一年开始的,则START列仅列出年份。不太精确。完全没有 除此之外,当我统计去年以来长时间运行的tmux会话的proc / id时,它报告了今年的日期。我去的解决方案:ps -eo pid,etime | grep $PID
Steven Lu

9

通过这种方式,您可以获得十个最旧进程的列表:

ps -elf | 排序-r -k12 | 头-n 10

实际上,这为您提供了10个最新的进程,因为默认情况下它显示STIME,即程序启动的时间。如果显示的是ETIME,这是自程序启动以来经过的时间,那么这是正确的。
Sarel Botha

8

Jodie C和其他人指出了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”。


这就是我所需要的。六年后,bash 5.0.3抱怨双括号。第一个双括号与单括号一起使用,而第二个在不引用变量的情况下也是如此。然后它对我有用。谢谢。
Bill McGonigle

7

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秒。


3

您可以bc在mob的答案中使用这两个命令,并获得自过程开始以来经过的秒数:

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>

如果pattern是一个字符串或扩展的正则表达式,它将打印出与此模式匹配的所有进程以及它们启动后的秒数。:)


2

做一个ps -aef。这将向您显示该过程开始的时间。然后使用date命令查找当前时间。计算两者之间的差异,以找到流程的期限。


不幸的是,这里的时间输出很难解析。对于短时间运行的进程,它可以是“ HH:MM”;对​​于非常长时间运行的过程,它可以是“ MonDD”(可能已本地化!),甚至是年份。
Slaven Rezic 2015年

1

我做了与接受的答案类似的操作,但略有不同,因为我想根据进程名称和运行超过100秒的错误进程进行匹配

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

$ RN不应该是$ NR吗?
cmjohns

1

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

以获取自该时间点以来的秒数的进程开始时间。与当前时间(date +%s)比较,以获取该过程的当前时间。


我们可以加入两个命令来获得秒进程启动:“回声stat -t /proc/<pid> | awk '{print $14}'- date +%s| BC”
拉斐尔S. Calsaverini

1
这并不总是正确的-至少对于Linux 2.6系统而言并非如此。我有一个过程始于9:49,但stat -t(和stat)表明它始于13:14。

@dpk:有时您有一个主进程和一些fork在运行。主进程应该是9:49,但是子进程可以有更多最近的时间。进程的线程也是如此。
higuita 2014年

0

使用ps是正确的方法。我之前已经做过类似的事情,但是没有方便的来源。通常-ps有一个选项可以告诉它要显示的字段以及要排序的字段。您可以按运行时间对输出进行排序,grep所需的进程,然后将其终止。

高温超导


0

如果有人需要用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;
}

0

来到某个地方..认为这是简单而有用的

您可以直接在crontab中使用该命令,

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

或者,我们可以将其编写为shell脚本,

#!/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

0

sincetime的@Rafael S.Calsaverini版本:

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

这将反转输出字段:首先是经过时间,第二是包括参数的完整命令。这是首选,因为full命令可能包含空格。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.