Tuesday, August 19, 2008

Finding Running Process ID's On Linux Using Pidof

Hey again,

Today, we're going to take a look at a quirky little command (The use of the term "quirky" is my personal bias creeping in ;) called "pidof" that you can, absolutely, find on RedHat ES versions 4 and up. It's probably on lots of other distro's, too, but I can only afford to muck with what's in the free-server-pool at any given moment. This command is actually very interesting and, given that RHEL3 boasts the "pgrep" command, I was quite surprised when I bumped into it. I'm not sure if it seemed contradictory or complementary; just unexpected.

"pidof," as the name would suggest, is a program that will find the process id (PID) of a running program (or the PID's of programs). Again, just like its name, it finds the "pid of" a running process. I'm just repeating this over and over again because the name is so self explanatory is seems contrary to everything I've come to expect from Linux and Unix (od, bc, dc, nm, ar, etc.. &;-- Literally etcetera. There's no program, that I know of, named etc yet ;)

Normally, I would blow a command like this off and stick with what I know; relegating it to that place in the back of my mind so cluttered with mental detritus that it's bound to all crumble apart sooner or later ;) As luck, or a fate I couldn't possibly avoid, would have it, this command does have some interesting (if not puzzling) features and an even more interesting lineage (skip to the bottom of the post for the most bizarre thing about it ;).

In it's basic form, you use it much like you might use pgrep. For instance:

host # pidof dsmadmc
27937 27927 27921 27918 24324 15596 11970 17360

the output format is somewhat different (horizontal rather than vertical) and more limited, but the results are relatively equal. You've just pulled a list of PID's that have, in this case, the string "dsmadmc" in them. It should be noted that pidof's standard mode of operation is like using "pgrep" with the "-x" option. It only returns exact matches and not substring matches. It should also be noted that, unlike pgrep, you can specify more than one program on the command line without making any major changes. Of course, the output is cryptic enough that this may or may not be a good idea:

host # pidof dsmadmc sshd
27937 27927 27921 27918 24324 15596 11970 17360 20657 20651 31593
host # pidof sshd
20657 20651 31593
<-- As you can see, the PID groups follow each other in order, but you have no way of knowing - at least from the straight output - where the PID's from your first named program stop and the PID's from your next named program begin!

This seems like it would be a great way to line up and knock down zombie processes, but, unfortunately, it won't match them (at least not the ways I tried):

host # ps -efl|grep Z
4 Z root 20992 20989 0 79 0 - 0 exit 00:00 ? 00:00:00 [sh] <defunct>
host # pidof Z

host # pidof defunct

host # pidof "<defunct>"

host # pidof "[sh]:

...and nothing.

If, for some reason, you only want to return one PID from a pool of them, you can run it with "-s," like so:

host # pidof -s dsmadmc

The only thing I could ascertain from running this option multiple times is that it just picks the first PID from it's normal output when you run it in "single shot" mode. I couldn't make any correlation between start date, run time, child/parent process relationship or anything more complex. It's actually as simple as it seems. I suppose you could use this option if you just needed to make sure that at least one instance of a program was running.

The "-x" option is good for hunting down what shell is running what script. You can usually find this out by backtracing the PID from child to parent and so forth, by running "file" against the script itself or just checking your ps output. In the case where you have a script, showing up in your process table, that doesn't have an obvious point of origin for you to examine, this option is a pretty quick way to figure out the invoking shell. Brownie points to anyone who can tell me why this is a complete waste of time in the example I'm giving here ;)

host # ps -ef|grep "[s]leeper"
user1 28884 20658 0 13:37 pts/0 00:00:00 /bin/bash ./sleeper
<-- It should be noted that this is the ps output from a straight run of a script with a shebang line of: #!/bin/bash.
host # pidof -x sleeper

Generally, that shell won't be hanging around once the shell script stops running. If it doesn't die, there's no way to backward-extrapolate the script it had previously run without prior knowledge.

And finally there's the "-o" option, which let's you "omit" a process, with a certain process id, from your output, like so:

host # pidof -o 30401 dsmadmc
30691 30381 30370 24324 15596 11970 17360

For multiple omissions, you can't just add more PID's, you need to preface each with a -o flag, like so:

host # pidof -o 30401 30691 dsmadmc
30691 30381 30370 24324 15596 11970 17360
<-- The wrong way
host # pidof -o 30401 -o 30691 dsmadmc
30381 30370 24324 15596 11970 17360

The special %PPID descriptor can be used to denote that you want to make sure the parent PID doesn't show up in your output. In this case, it doesn't make any difference:

host # pidof -o %PPID dsmadmc
30691 30401 30381 30370 24324 15596 11970 17360
host # pidof dsmadmc
30691 30401 30381 30370 24324 15596 11970 17360

And, of course, just like pgrep and pkill, it's always best to be as specific as you can when naming the process you want to get the PID of. We noted above that this program is pretty good at making exact matches (no substrings matches - so "ini" won't match "init"), but you can never be too careful. Depending upon the process name, you may end up listing out PID's you didn't intend to (especially since there's no option to show both PID's and process names). For instance, if you went looking for a shell, you'd probably be running a high risk of returning false positives, like this:

host # pidof sshd
20657 20651 31593
host # ps -ef|grep "[s]shd"
root 31593 1 0 Apr11 ? 00:06:22 /usr/sbin/sshd
root 20651 31593 0 12:46 ? 00:00:00 sshd: user1 [priv]
user1 20657 20651 0 12:46 ? 00:00:00 sshd: user1@pts/0

Oh, yes, and that thing I mentioned at the beginning about "pidof" having an interesting lineage. As it turns out, this command is really just a symbolic link to the "killall5" command:

host # ls -l /sbin/pidof
lrwxrwxrwx 1 root system 8 Feb 8 2007 /sbin/pidof -> killall5

The "killall5" program's sole function is to send a signal (take your pick) to all processes except for those within the shell from which it's called. The reason this is interesting (at least to me) is that there is no option to send a signal, of any kind, in "pidof." This seems contrary to my sense of style, but arguments can be made for why this functionality was left out of "pidof." I think probably the most reasonable one is that "pidof" appears to have been written as an output producer only. A program that spits up lines of ordered and spaced numbers with the intention of having them "dealt with" by some other program on the other end of a pipe. Still, it seems a bizarre distinction to make.

But, enough of my belly-aching, "pidof" may be just the thing you're looking for. Who am I to judge? I've been around *nix too long to have a truly "objective" opinion about much of it ;)


, Mike
