in reply to Better way to search in the process table?

G'day Karl,

My main issues with the way you're currently doing this is that you're reading the entire process table for every ancestry level you want and, for each of those levels, you're repeating almost identical code.

Here's a technique that only reads the process table once and allows access to the process data in a variety of ways (I've shown a few examples). I've used your data (and some extra for testing) in a dummy AoH reference ($table) rather than attempting to recreate a Proc::ProcessTable object: you'll need to make changes (e.g. '$_->{pid}' to '$_->pid') for a real solution. Also note that by changing the value of $ancestry, you can get whatever number of ancestry levels you want; although, you may want to rethink my constant names if you start using 'GREATGREAT...GREATGRANDPARENT'. :-)

#!/usr/bin/env perl use strict; use warnings; use constant { CHILD => 0, PARENT => 1, GRANDPARENT => 2, GREATGRANDPARENT => 3, }; my $table = [ { pid => 0, ppid => 0, cmd => 'root' }, { pid => 603, ppid => 0, cmd => 'cron_parent' }, { pid => 18421, ppid => 603, cmd => 'cron_child' }, { pid => 18423, ppid => 18421, cmd => 'sh_mon' }, { pid => 18425, ppid => 18423, cmd => 'sh_kit' }, { pid => 18444, ppid => 18425, cmd => 'java' }, { pid => 28421, ppid => 603, cmd => 'cron_child' }, { pid => 28423, ppid => 18421, cmd => 'sh_mon' }, { pid => 28425, ppid => 18423, cmd => 'sh_kit' }, { pid => 28444, ppid => 18425, cmd => 'java' }, { pid => 28445, ppid => 18425, cmd => 'java' }, { pid => 99999, ppid => 18421, cmd => 'java' }, { pid => 2, ppid => 1, cmd => 'unwanted' }, ]; my $ancestry = 3; my %tree; { my %all; for (@$table) { $all{$_->{pid}} = $_; $tree{$_->{pid}} = [ $_ ] if $_->{cmd} =~ /java/; } for my $child (keys %tree) { my $ppid = $all{$child}{ppid}; for (1 .. $ancestry) { push @{$tree{$child}}, {%{$all{$ppid}}}; $ppid = $all{$ppid}{ppid}; } } } use Data::Dump; print "*** All parents ***\n"; dd [ map { $tree{$_}[PARENT] } sort keys %tree ]; print "*** 18444 grandparent ***\n"; dd $tree{18444}[GRANDPARENT]; print "*** 18444 greatgrandparent ***\n"; dd $tree{18444}[GREATGRANDPARENT]; print "*** All ancestries ***\n"; dd \%tree;

Output:

*** All parents *** [ { cmd => "sh_kit", pid => 18425, ppid => 18423 }, { cmd => "sh_kit", pid => 18425, ppid => 18423 }, { cmd => "sh_kit", pid => 18425, ppid => 18423 }, { cmd => "cron_child", pid => 18421, ppid => 603 }, ] *** 18444 grandparent *** { cmd => "sh_mon", pid => 18423, ppid => 18421 } *** 18444 greatgrandparent *** { cmd => "cron_child", pid => 18421, ppid => 603 } *** All ancestries *** { 18444 => [ { cmd => "java", pid => 18444, ppid => 18425 }, { cmd => "sh_kit", pid => 18425, ppid => 18423 }, { cmd => "sh_mon", pid => 18423, ppid => 18421 }, { cmd => "cron_child", pid => 18421, ppid => 603 }, ], 28444 => [ { cmd => "java", pid => 28444, ppid => 18425 }, { cmd => "sh_kit", pid => 18425, ppid => 18423 }, { cmd => "sh_mon", pid => 18423, ppid => 18421 }, { cmd => "cron_child", pid => 18421, ppid => 603 }, ], 28445 => [ { cmd => "java", pid => 28445, ppid => 18425 }, { cmd => "sh_kit", pid => 18425, ppid => 18423 }, { cmd => "sh_mon", pid => 18423, ppid => 18421 }, { cmd => "cron_child", pid => 18421, ppid => 603 }, ], 99999 => [ { cmd => "java", pid => 99999, ppid => 18421 }, { cmd => "cron_child", pid => 18421, ppid => 603 }, { cmd => "cron_parent", pid => 603, ppid => 0 }, { cmd => "root", pid => 0, ppid => 0 }, ], }

[While I can see the Proc::ProcessTable documentation uses indirect object notation, please be aware this syntax is strongly discouraged (explained in perlobj: Invoking Class Methods).]

-- Ken

Replies are listed 'Best First'.
Re^2: Better way to search in the process table?
by karlgoethebier (Abbot) on Mar 03, 2014 at 15:05 UTC

    Hello kcott and thank you very much for your elegant solution!

    Here is what i worked out:

    #!/usr/bin/perl use strict; use warnings; use Proc::ProcessTable; use Data::Dump; my $processes = new Proc::ProcessTable; my $ancestry = 3; my %tree; my %all; for ( @{ $processes->table } ) { $all{ $_->pid } = $_; $tree{ $_->pid } = [$_] if $_->cmndline =~ /^\/usr\/sbin\/mysqld/; } # dd \%all; exit; # dd \%tree; exit; for my $child (keys %tree) { print qq(child:\t$child\n); my $ppid = $all{$child}->ppid; print qq(ppid:\t$ppid\n); for (1 .. $ancestry) { push @{$tree{$child}}, $all{$ppid}; $ppid = $all{$ppid}{ppid}; last unless $ppid; print qq(ppid:\t$ppid\n); } } dd \%tree; __END__

    This seems to work.

    If $ancestry == 3 and i don't say last unless $ppid, it goes back until PID 0 and sets one key in %tree to undef. I hope i didn't miss something.

    Thanks again for sharing your knowledge and best regards,

    Karl

    «The Crux of the Biscuit is the Apostrophe»

      "If $ancestry == 3 and i don't say last unless $ppid, it goes back until PID 0 and sets one key in %tree to undef. I hope i didn't miss something."

      The keys in %tree are unaffected by that statement: they're already set and you're looping through them (i.e. for my $child (keys %tree) {...}). You'll need to clarify this. You're probably referring to one of these: the value of the key (normally an arrayref); an element in that arrayref (normally a hashref); a key/value in one of those hashrefs.

      I'd normally expect the parent of PID 0 to be PID 0 also. That's true for my OS but perhaps it's not the case on your OS. It's possible that Proc::ProcessTable does not return information for PID 0: I claim no particular experise with this module, you'll need to research this yourself. It could also be a security feature: maybe only UID 0 (root) can query PID 0.

      If the code you currently have does what you want, perhaps just keep to the old adage: "If it ain't broke, don't fix it." :-)

      -- Ken

        "...they're already set"

        Yes. D'oh! I don't known why i wrote this nonsens :-(

        # output shortend $anchestry =3 ; # last unless $ppid; { 9675 => [ bless({ cmndline => "/usr/sbin/mysqld", pid => 9675, ppid => 9473, }, "Proc::ProcessTable::Process"), bless({ cmndline => "/bin/sh /usr/bin/mysqld_safe", pid => 9473, ppid => 1, }, "Proc::ProcessTable::Process"), bless({ cmndline => "init [3] ", pid => 1, ppid => 0, }, "Proc::ProcessTable::Process"), undef, <-- last slot ], } $anchestry = 3 ; last unless $ppid; { 9675 => [ bless({ cmndline => "/usr/sbin/mysqld", pid => 9675, ppid => 9473, }, "Proc::ProcessTable::Process"), bless({ cmndline => "/bin/sh /usr/bin/mysqld_safe", pid => 9473, ppid => 1, }, "Proc::ProcessTable::Process"), bless({ cmndline => "init [3] ", pid => 1, ppid => 0, }, "Proc::ProcessTable::Process"), ], # OK }

        It works.

        Thank you and best regards, Karl

        «The Crux of the Biscuit is the Apostrophe»

        I'll post tomorrow what i figured out because i don't have access to this machine tonight.

        Best regards and thank you, Karl

        «The Crux of the Biscuit is the Apostrophe»