4

以下代码片段用于查找用户终端的 PID,方法是使用 ptree 并从它返回的结果中获取第三个PID。所有终端 PID 都以用户登录作为密钥存储在哈希中。

   ## If process is a TEMINAL.
   ## The command ptree is used to get the terminal's process ID.
   ## The user can then use this ID to peek the user's terminal.
   if ($PID =~ /(\w+)\s+(\d+) .+basic/) {
    $user = $1;
    if (open(PTREE, "ptree $2 |")) {
     while ($PTREE = <PTREE>) {
      if ($PTREE =~ /(\d+)\s+-pksh-ksh/) {
       $terminals{$user} = $terminals{$user} . " $1";
       last;
      }
      next;
     }
     close(PTREE);
    }
    next;
   }

下面是一个示例 ptree 执行:

ares./home_atenas/lmcgra> ptree 29064
485   /usr/lib/inet/inetd start
  23054 /usr/sbin/in.telnetd
    23131 -pksh-ksh
      26107 -ksh
        29058 -ksh
          29064 /usr/ob/bin/basic s=61440 pgm=/usr/local/etc/logon -q -nr trans
            412   sybsrvr

我想知道是否有更好的编码方法。这是脚本中运行时间最长的部分。

注意:此代码与其他代码片段一起位于循环内并执行了几次。

4

6 回答 6

5

我认为主要问题是这段代码在循环中。您无需多次运行 ptree 并解析结果!您需要想出一种方法来运行一次 ptree 并将其放入以后可以使用的数据结构中。可能是某种简单的哈希就足够了。您甚至可以只保留 %terminals 哈希并继续重用它。

一些挑剔...

  • 你的两个“下一个”陈述对我来说似乎都是不必要的......你应该能够删除它们。

  • 代替

    $terminals{$user} = $terminals{$user} . " $1";
    

和:

    $terminals{$user} .= " $1";
  • 将您用作文件句柄的裸字 PTREE 替换为 $ptreeF 或类似的东西......大约 10 年前,文件句柄不需要使用裸字:)

  • 我不知道为什么你的 $PID 变量都是大写的......它可能会让你的代码的读者感到困惑,因为看起来这个变量有一些特别的东西,但实际上并没有。

于 2008-12-26T12:42:16.923 回答
4

我认为通过避免重复执行外部命令(ptree在本例中为 )的开销,您将获得最佳的性能改进。ptree我会寻找一个为正在读取的数据结构提供直接接口的 CPAN 模块。检查Linux::命名空间,也许?(我不确定是否ptreesetuid;这可能会使事情复杂化。)

除了上面的建议,一些额外的样式和健壮性注释仅基于发布的片段(如果更大的代码使它们无效,请原谅我):

  • strict至少 我会从使用开始。词法文件句柄也是一个好主意。

  • 当您无法open()执行ptree命令时,您似乎在默默地忽略大小写。发生这种情况的原因有很多,其中一些我无法想象你会想忽略,比如……</p>

  • 您没有使用ptree命令的完整路径,而是假设它在您的路径中——并且您路径中的路径是正确的。

于 2008-12-26T13:58:06.413 回答
3

我正在考虑使用 ps 来获取父母的 pid,但我需要循环它来获取曾祖父母的 pid。那是我需要的。谢谢。– 拉姆克罗


对不起,有很多用户,每个人最多可以打开三个终端。整个脚本用于查找正在使用文件的终端。我使用 fuser 来查找使用文件的进程。然后使用 ptree 查找终端的 pid。– 拉姆克罗


如果您有(或可以获得)使用文件的 PID 列表,并且只需要该 PID 的所有祖父母,那么肯定有一种更简单的方法。

#!perl

use warnings;
use strict;

#***** these PIDs are gotten with fuser or some other method *****
my($fpids) = [27538, 31812, 27541];

#***** get all processes, assuming linux PS *****
my($cmd) = "ps -ef";
open(PS, "$cmd |") || die qq([ERROR] Cannot open pipe from "$cmd" - $!\n);

my($processlist) = {};
while (<PS>) {
    chomp;

    my($user, $pid, $ppid, $rest) = split(/ +/, $_, 4);
    $processlist->{$pid} = $ppid;
}

close PS;

#***** lookup grandparent *****
foreach my $fpid (@$fpids) {
    my($parent) = $processlist->{$fpid} || 0;
    my($grandparent) = $processlist->{$parent} || 0;

    if ($grandparent) {
        #----- do something here with grandparent's pid -----
        print "PID:GRANDPID - $fpid:$grandparent\n";
    }
    else {
        #----- some error condition -----
        print "ERROR - Cannot determine GrandPID: $fpid ($parent)\n";
    }
}

这对我来说产生:

ERROR - Cannot determine GrandPID: 27538 (1)
PID:GRANDPID - 31812:2804
PID:GRANDPID - 27541:27538
于 2008-12-26T17:30:36.670 回答
3

系统上有多少用户?你能把这个倒过来吗?列出系统中的所有 -pksh-ksh 进程及其 EUID,并从中构建映射 - 这可能只是 ps/ptree 的一次执行。

于 2008-12-26T14:02:11.053 回答
2

您是否考虑过使用 ' who -u' 来告诉您哪个进程是给定 tty 的登录 shell 而不是 using ptree?这将简化您的搜索 - 无论您还应该进行哪些其他更改。

于 2008-12-26T14:17:09.277 回答
1

我只是根据您的脚本在这里做了一些琐碎的计时(调用“cat ptree.txt”而不是 ptree 本身)并证实了我的想法,即您所有的时间都花在了创建新的子进程和运行 ptree 本身上。除非您可以考虑调用 ptree 的需要(也许有一种方法可以打开连接一次并重复使用它,例如使用 nslookup),否则您不会看到任何真正的收益。

于 2008-12-26T14:08:24.707 回答