Process Management – How to Check if a Given PID is Running

perlprocprocessprocess-management

I'm writing a Perl script that parses logfiles to collect PIDs and then checks whether that PID is running. I am trying to think of the best way to make that check. Obviously, I could do something like:

system("ps $pid > /dev/null") && print "Not running\n";

However, I'd prefer to avoid the system call if possible. I therefore thought I could use the /proc filesystem (portability isn't a concern, this will always be running on a Linux system). For example:

if(! -d "/proc/$pid"){
    print "Not running\n";
}

Is that safe? Can I always asume that if there's no /proc/$pid/ directory the associated PID is not running? I expect so since AFAIK ps itself gets its information from /proc anyway but since this is for production code, I want to be sure.

So, can there be cases where a running process has no /proc/PID directory or where a /proc/PID directory exists and the process is not running? Is there any reason to prefer parsing ps over checking for the existence of the directory?

Best Answer

The perl function kill(0,$pid) can be used.

If the return code is 1 then the PID exists and you're allowed to send a signal to it.

If the return code is 0 then you need to check $!. It may be EPERM (permission denied) which means the process exists or ESRCH in which case the process doesn't exist.

If your checking code is running as root then you can simplify this to just checking the return code of kill; 0=>error, 1=>ok

For example:

% perl -d -e 0

Loading DB routines from perl5db.pl version 1.37
Editor support available.

Enter h or 'h h' for help, or 'man perldebug' for more help.

main::(-e:1):   0
  DB<1> print kill(0,500)
0
  DB<2> print $!
No such process
  DB<3> print kill(0,1)
0
  DB<4> print $!
Operation not permitted
  DB<5> print kill(0,$$)
1

This can be made into a simple function

use Errno;

sub test_pid($)
{
  my ($pid)=@_;

  my $not_present=(!kill(0,$pid) && $! == Errno::ESRCH);

  return($not_present);
}

print "PID 500 not present\n" if test_pid(500);
print "PID 1 not present\n" if test_pid(1);
print "PID $$ not present\n" if test_pid($$);