Showing posts with label pid. Show all posts
Showing posts with label pid. Show all posts

Tuesday, March 31, 2009

Measuring Heavy CPU Usage Over Time On Linux And Unix

Hey There,

Today's bash script is going to be somewhat related to our previous script which tracked idle process time on Linux and Unix, insofar as it deals with trying to rid your system of troubling processes as automatically as possible. Of course, there's no substitute for an eyeball-inspection (of the system, I mean. Unless your eyeballs are hurting ;) but, once you've got a few things down and feel reasonably safe, the more you can take off of your daily plate, the better. ...Just don't make your job so incredibly simple that a machine (or a pre-schooler from a third-world country) could do it ;)

This script is, like most of the stuff we put out here, incredibly easy to run (especially since you set all the variables inside it - change as you see fit), like so:

host # ./munchies

And you're off. In the screenshots below, we'll walk through some basic examples of simple usage, assuming the script's built in parameters. Any process consuming more than 10 percent of the CPU gets added to the blacklist and any process that shows up in the blacklist, 10 times consecutively, will get killed (No screwin' around here ;)

In the first screenshot, we've isolated process id 499 (which happens to be the X server), since it's the only process on the box that meets the "CPU percentage" criteria. Once it finds that process, it adds it to the default temporary file (the simple way to maintain state ;). We then populate the /tmp/munchiestats file with a whole bunch of other PID's (some real, some non-existent) and multiple instances of PID 499 (but, less than 9, so we don't trigger the kill on the next execution) and cat that so you can see the contents:

Click on the picture below. Like water on a sponge ;)

munchies script output 1

In the second screenshot, we run munchies again and see it clear all the PID's in the temp file that are legitimate, but aren't using over 10% of the CPU anymore. We also free any PID's in the temp file that don't exist any more (possibly, from a process exiting, but - in this case - because we just made them up ;). The final run executes the kill of PID 499 and removes it from the temp file:

Click on the picture below and brace yourself for the HUGEness ;)

munchies script output 2

Of course, the script has its faults. The most blatant pain in the arse (to our thinking, at this point - with very little QA'ing done ;) is that we've hardcoded the percentage of CPU (10%) and amount of times a PID is allowed to use that much (10 times) and not made them command line or top-listing variables. If you want to change it in the script, just modify these lines:

For the CPU percentage limit:

if [[ $cpu_percentage_integer -gt 10 ]]

And for the number of consecutive times you'll allow the offending PID to get away with it before you murder (I mean, kill... ;) it:

if [[ $chronic_muncher -gt 8 ]] <-- This is set to 8 since, if the pre-existing number of additions of a certain PID is over 8, it's (at best) 9, and this go 'round will put it at the limit of 10!
elif [[ $chronic_muncher -lt 10 ]]

Another maybe-flaw is that we don't have it set to run backgrounded, or as a daemon. In other words, you need to run it on your own schedule. We have it running in cron every 5 minutes, so a process can abuse the CPU for about 50 minutes before we kill it. If you run it every minute, you can kill it in 10. Of course, all of this is "variable" and you can change it to suit your needs.

And, if you consider this a flaw, the script was written in bash on Solaris 10, but should be easily portable to other Unix and Linux distro's. Let us know if you'd like to see a version for RedHat or Ubuntu!

Here's hoping this helps you out in some way, shape or form. It's probably translatable to a lot of other work-type performance-tuning situations, as well.

Cheers!


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/bin/bash

#
# munchies - eat up processes using over 10 percent of the cpu over 10 iterations...
#
# 2009 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

cpu_munchers_file="/tmp/munchiestats"
sed=`which sed`
awk=`which awk`
ps="/usr/ucb/ps" # Using /usr/ucb/ps on purpose for %CPU stats
grep=`which grep`
mv=`which mv`
wc=`which wc`
sort=`which sort`
xargs=`which xargs`
kill=`which kill`
# kill_signal="-9" # Don't set this if "kill -TERM"/"kill -15" - i.e. "plain vanilla"kill - is acceptable

while read a b c d
do
munch_pid="$b"
cpu_percentage="$c"

if [[ -z "$cpu_percentage" ]]
then
echo "munch_pid $munch_pid is either non-existent or is using less than zero percent of the cpu!"
continue
else
cpu_percentage_integer=$(echo "$cpu_percentage"|$sed 's/^\([^\.]*\)\..*$/\1/')
fi

if [[ $cpu_percentage_integer -gt 10 ]]
then
echo "Got A Bad One Here - munch_pid $munch_pid Is Using $cpu_percentage_integer Percent Of Our Cpu"
if [[ -f $cpu_munchers_file ]]
then
echo "Checking cpu_munchers_file $cpu_munchers_file For munch_pid $munch_pid"
chronic_muncher=$(echo `$grep -w $munch_pid $cpu_munchers_file|$wc -l`)
if [[ $chronic_muncher -gt 8 ]]
then
echo "munch_pid $munch_pid Count Is $chronic_muncher - This Will Put It At 10 Or Higher"
echo "Issuing \"$kill $kill_signal $munch_pid\" And Removing From $cpu_munchers_file now!"
temp_variable=$$
### $kill $kill_signal $munch_pid
$grep -vw $munch_pid $cpu_munchers_file >>${cpu_munchers_file}.$temp_variable
mv ${cpu_munchers_file}.$temp_variable $cpu_munchers_file
elif [[ $chronic_muncher -lt 10 ]]
then
echo "munch_pid $munch_pid, with $cpu_percentage_integer cpu usage, Being Added, Possibly Again, To cpu_munchers_file $cpu_munchers_file"
echo "$munch_pid" >>$cpu_munchers_file
fi
else
echo "No Cpu-Munchers Exist. Creating cpu_munchers_file $cpu_munchers_file And Adding munch_pid $munch_pid"
echo "$munch_pid" >>$cpu_munchers_file
fi
else
if [[ -f $cpu_munchers_file ]]
then
chronic_muncher=$(echo `$grep -w $munch_pid $cpu_munchers_file|$wc -l`)
if [[ $chronic_muncher -gt 0 ]]
then
echo "munch_pid $munch_pid Is Ok And Is In $cpu_munchers_file - Removing"
temp_variable=$$
$grep -vw $munch_pid $cpu_munchers_file >>${cpu_munchers_file}.$temp_variable
mv ${cpu_munchers_file}.$temp_variable $cpu_munchers_file
else
:
fi
else
:
fi
fi
done <<< "`$ps -aux|$awk '{print $1,$2,$3,$NF}'|sed 1d`"

echo "Checking $cpu_munchers_file For Non-Existent munch_pids"
if [[ -f $cpu_munchers_file ]]
then
muncher_array=$($sort -u $cpu_munchers_file|$xargs echo)
for possible_lost_pid in ${muncher_array[@]}
do
is_this_muncher_real=$(echo `$ps -aux|$grep -w $possible_lost_pid|$grep -v grep|$wc -l`)
if [[ $is_this_muncher_real -eq 0 ]]
then
echo "Lost munch_pid $possible_lost_pid Is No Longer Running. Removing From $cpu_munchers_file"
temp_variable=$$
$grep -vw $possible_lost_pid $cpu_munchers_file >>${cpu_munchers_file}.$temp_variable
mv ${cpu_munchers_file}.$temp_variable $cpu_munchers_file
fi
done
echo "All Possible Injustices Have Been Remedied"
fi



, Mike




Discover the Free Ebook that shows you how to make 100% commissions on ClickBank!



Please note that this blog accepts comments via email only. See our Mission And Policy Statement for further details.

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
F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD
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
27937


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
28884


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 ;)

Cheers,

, Mike




Please note that this blog accepts comments via email only. See our Mission And Policy Statement for further details.

Thursday, May 15, 2008

Finding An "Invisible" Proc's Working Directory Without lsof On Linux Or Unix

Ahoy there,

Today, we're going to take a look at something that gets taken for granted a lot these days. lsof (a fine program, to be sure. No debate here) has become a very common staple for finding out information about processes, and where they're hanging out, on most Linux and Unix systems today. Much like the command "top," it provides a simple and robust frontend to having to do a lot of grunt-work to achieve the same results.

I find that, for the most part, lsof is used to find out where a process is, or what filesystems, etc, it's using, in order to troubleshoot issues. One of the most common is the "mysteriously full, yet empty, disk" phenomenon. Every once in a while that will turn out to be an issue where all of the inodes in a partition have been used before all of the blocks have, which produces confusing output in df, leading to the mistaken assumption that there is plenty of space left a device even when there isn't.

However, many times, that empty-yet-full disk is the victim of a process that met an untimely demise and never cleaned up a lot of temporary space in memory (or virtual disk, to split hairs). Another issue that lsof is used for is to find out which dag-nabbed process is holding onto a mount-point that claims it's in use when no one is logged on and no user processes are running that would access it (for instance, a really specific, user-defined, mountpoint like /whereILikeToPutMyStuff - Hopefully the OS isn't depending on this to be around ;) Both problems are, essentially, the same.

However, should you find yourself in a situation where lsof either doesn't come with your Operating System, and/or hasn't been installed, you can still break down these two (and I'm just limiting the post to these two particulars so I don't end up writing an embellished manpage ;) separate issues into one, and find the solution to your problems using the commonly available "pwdx" utility.

pwdx will print out the working directory of any given process (using the process ID as input) at its best. But this is enough to get you to the answer you need.

For instance, we'll take this common scenario: /tmp is reporting 100% full, but df -k shows that /tmp is only at 1% capacity (99% of it is unused). My thinking here almost immediately gravitates toward vi, or some other program that opened up a buffer in memory (using /tmp or /var/tmp), got clipped unexpectedly and never let the system know that it was done with the space it allocated for itself. This would normally not be an issue but, since your Linux or Unix machine "thinks" /tmp is full, whether or not it actually is makes no difference. It won't let you use the free space :(

This command line could be used to figure out what process was using that space in /tmp or /var/tmp:

host # ps -ef|awk '{print $2}'|xargs pwdx 2>&1|grep -iv cannot|grep /tmp
2969: /tmp


Taking it a step further (assuming we trust our own output), we could just skip right to the process in question by adding a bit more to the pipe-chain:

host # ps -ef|awk '{print $2}'|xargs pwdx 2>&1|grep -iv cannot|grep /tmp|sed 's/^\([^:]*\).*$/\1/'|xargs -n1 ps -fp
UID PID PPID C STIME TTY TIME CMD
root 2969 2966 0 Mar 21 ? 0:05 /bin/vi /home/george/myHumungousFile


Since it's May already, we can fairly assume that this PID is pointing to a dead process (especially since it has no TTY associated with it), and (double-checking, just to be sure) we can probably solve our problem by killing that PID. See our previous post on killing zombie processes if it won't seem to go away and "ps -el" shows it in a Z state.

Yes, that example was pretty simplistic, but the same methodology can be used to find other programs using up other filesystems. Just like "lsof -d," you'll be able to find out what processes are using what filesystems and narrow down your list of suspects, if you don't nail the correct one right away. Since pwdx comes with your Linux or Unix OS, it's actually statistically more likely than lsof to be correct about what process is using what filesystem :)

Cheers,

, Mike

Monday, December 31, 2007

Network Port Querying Script

Hey there,

The script I've put together here was originally written to meet a certain demand. That demand was actually my own, but that's beside the point ;)

This script should come in useful for you if you ever need to query a port and find out what's going on with it (like who's using it and/or what process id is associated with it). It's simple to invoke (taking only the port number as its argument) and produces information that can be a great aid in troubleshooting network connection issues.

If you refer back to this previous post you can check out a small walkthrough regarding how to query a port using lsof and/or the proc commands. This script uses lsof also, but combines it with netstat to produce output in an easy to read format, while grabbing a little more information in the process. Assuming we call it portquery, it can be invoked like this:

host # ./portquery 22 <--- Let's just see what's going on with SSH

and it will produce output for you like the following. Note that it produces a formatted output block for every single process connected to a port. On a high-traffic machine, checking SSH might produce a few pages of output. This is what it looks like when it's run:

Port 22 Information :
Service = sshd
PID = 469
User = root
Protocol = TCP
Status = LISTEN
Port 22 Information :
Service = sshd
PID = 469
User = jimmy88
Protocol = TCP
Status = LISTEN


...and the list goes on to print out information blocks for every PID attached to that port. This script has been a great help for me not only in that it makes a manual process automatic, but also in that it's easy for other non-admins to read.

Here's hoping you have some use for it :)

Best Wishes,


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/bin/ksh

#
# 2007 - Mike Golvach - eggi@comcast.net
#
# Usage: portquery [port number]
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

trap 'exit' 1 2 3 9 15
if [ $# -ne 1 ]
then
echo "Usage: $0 portNumber"
exit 1
fi

portnumber=$1

/bin/netstat -a |grep -w "$portnumber" >/dev/null 2>&1

if [ $? -ne 0 ]
then
echo "Nothing's listening on - or using - port $portnumber"
exit 1
fi

/usr/local/bin/lsof 2>&1|grep -v "^lsof:"|grep -w $portnumber 2>&1|while read x
do
portinfo=`echo $x|awk '{print $1 " " $2 " " $3 " " $4 " " $5 " " $6 " " $7 " " $8 " " $9 " " $10}'`
echo "Port $portnumber Information :"
echo " Service = `echo $portinfo|awk '{print $1}'`"
echo " PID = `echo $portinfo|awk '{print $2}'`"
echo " User = `echo $portinfo|awk '{print $3}'`"
echo " Protocol = `echo $portinfo|awk '{print $8}'`"
echo " Status = `echo $portinfo|awk '{print $10}'|sed 's/(//'|sed 's/)//'`"
done



, Mike