如何在Perl中快速找到用户的终端PID?

时间:2008-12-26 08:47:36

标签: perl terminal pid

以下代码片段用于查找用户终端的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

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

注意:此代码以及其他代码段位于循环内并执行几次。

6 个答案:

答案 0 :(得分:5)

我认为主要问题是此代码处于循环中。您不需要运行ptree并多次解析结果!您需要找到一种方法来运行ptree一次并将其放入一个稍后可以使用的数据结构中。可能是某种简单的哈希就足够了。您甚至可以只保留%终端哈希并继续重复使用它。

一些挑剔...

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

  • 替换

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

使用:

    $terminals{$user} .= " $1";
  • 替换您的裸字PTREE 用作文件句柄 $ ptreeF或其他一些......使用 裸露变得不必要了 filehandles大约10年前:)

  • 我不知道为什么你的$ PID变量 是全部大写......这可能令人困惑 对你的代码的读者,因为它 看起来有什么东西 特别关于那个变量,和 没有。

答案 1 :(得分:4)

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

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

  • 我开始使用strict,至少。 Lexical filehandles也是一个好主意。

  • 当您无法open() ptree命令时,您似乎默默地忽略了这种情况。这可能有很多原因,其中一些我无法想象你想忽视,例如...

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

答案 2 :(得分:3)

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

答案 3 :(得分:3)

我正在考虑使用ps来获取父母的pid,但是我需要循环这个以获得曾祖父母的pid。这就是我需要的那个。谢谢。 - lamcro


抱歉,有很多用户,每个用户最多可以打开三个终端。整个脚本用于查找正在使用文件的终端。我使用fuser来查找使用文件的进程。然后使用ptree查找终端的pid。 - lamcro


如果您已经(或可以获得)使用文件的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

答案 4 :(得分:2)

您是否考虑使用“who -u”告诉您哪个进程是给定tty的登录shell而不是ptree?这样可以简化您的搜索 - 无论您还应该做出哪些其他更改。

答案 5 :(得分:1)

我刚刚根据你的脚本(称为“cat ptree.txt”而不是ptree本身)做了一些微不足道的计时,并确认了我的想法,你所有的时间都花在创建新的子流程和运行ptree本身上。除非你可以考虑不需要调用ptree(也许有一种方法可以打开连接并重用它,就像使用nslookup一样),你不会看到任何真正的收益。