Как я могу быстро найти PID терминала пользователя в Perl? - PullRequest
4 голосов
/ 26 декабря 2008

Следующий фрагмент кода используется для поиска PID терминала пользователя, используя ptree и извлекая третий 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;
   }

Ниже приведен пример выполнения дерева:

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 ]

5 голосов
/ 26 декабря 2008

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

Некоторые кусачки ...

  • Кажется, что ваши "следующие" утверждения ненужный для меня ... вы должны быть возможность просто удалить их.

  • Заменить

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

с:

    $terminals{$user} .= " $1";
  • Замените голое слово PTREE, которое вы использовать в качестве файлового дескриптора с $ ptreeF или что-то подобное ... используя голые слова стали ненужными для файловые ручки около 10 лет назад :)

  • Я не знаю, почему ваша переменная $ PID это все заглавные буквы ... это может сбить с толку читателям вашего кода, потому что это похоже, что-то есть Особо об этой переменной, и нет.

4 голосов
/ 26 декабря 2008

Я думаю, что вы получите лучшее улучшение производительности, избегая лишних затрат на повторное выполнение внешней команды (ptree, в данном случае). Я бы искал модуль CPAN, который обеспечивает прямой интерфейс со структурами данных, которые ptree читает. Проверьте Linux :: пространство имен, может быть? (Я не уверен, если ptree это setuid ; это может усложнить ситуацию.)

Приведенный выше совет, некоторые дополнительные примечания по стилю и надежности, основанные только на опубликованном фрагменте (простите, если код большего размера делает их недействительными):

  • Я бы начал с использования strict, по крайней мере. Лексические файловые дескрипторы также будет хорошей идеей.

  • Вы, похоже, молча игнорируете случай, когда вы не можете open() команду ptree. Это может произойти по многим причинам, некоторые из которых я не могу себе представить, что вы хотите игнорировать, например ...

  • Вы не используете полный путь к команде ptree, а скорее предполагаете, что он находится на вашем пути, и что тот, что на вашем пути, является правильным.

3 голосов
/ 26 декабря 2008

Я думал об использовании 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
3 голосов
/ 26 декабря 2008

Сколько пользователей в системе? Вы можете инвертировать это? Перечислите все процессы -pksh-ksh в системе вместе с их EUID и создайте карту из этого - это может быть только одно выполнение ps / ptree.

2 голосов
/ 26 декабря 2008

Рассматривали ли вы использование 'who -u', чтобы сказать вам, какой процесс является оболочкой входа для данного tty вместо использования ptree? Это упростит ваш поиск - независимо от других изменений, которые вы также должны внести.

1 голос
/ 26 декабря 2008

Я только что выполнил некоторые тривиальные моменты времени на основе вашего сценария (вызывая «cat ptree.txt» вместо самого ptree) и подтвердил мои мысли о том, что все ваше время уходит на создание новых подпроцессов и запуск самого ptree. Если вы не можете устранить необходимость вызова ptree (возможно, есть способ открыть соединение один раз и использовать его повторно, как в nslookup), вы не увидите никаких реальных выгод.

...