You’ve probably seen it before. There’s a process that is marked with a “Z” in ps
/top
… the infamous Zombie process. And true to it’s name, you can’t kill it. Even as root. It just sits there mocking you.
Sure, you could reboot, but what’s the fun in that? If you know how to, and have the right tools, you can take care of it yourself.
How? By using the GDB debugger and a simple script. Originally found here, I made a few changes to improve it.
We’ll call it the Zombie Slayer.
Prepping the Slayer
First, the GDB debugger needs to be installed.
sudo apt-get install gdb
sudo dnf install gdb
sudo pacman -Sy gdb
Then save the below program to your path, as /usr/local/bin/zombie-slayer
#!/bin/bash
##################################################################
# Script: Zombie Slayer
# Author: Mitch Milner
# Date: 03/13/2013 ---> A good day to slay zombies
#
# Requirements: yum install gdb
# permissions to attach to the parent process
#
# This script works by using a debugger to
# attach to the parent process and then issuing
# a waitpid to the dead zombie. This will not kill
# the living parent process.
##################################################################
clear
# Wait for user input to proceed, give user a chance to cancel script
echo "***********************************************************"
echo -e "This script will terminate all zombie process."
echo -e "Press [ENTER] to continue or [CTRL] + C to cancel:"
echo "***********************************************************"
read cmd_string
echo -e "\n"
# initialize variables
intcount=0
lastparentid=0
# remove old gdb command file
rm -f /tmp/zombie_slayer.txt
# create the gdb command file
echo "***********************************************************"
echo "Creating command file..."
echo "***********************************************************"
ps -e -o ppid,pid,stat,command | grep Z | sort | while read LINE; do
intcount=$((intcount+1))
parentid=`echo $LINE | awk '{print $1}'`
zombieid=`echo $LINE | awk '{print $2}'`
verifyzombie=`echo $LINE | awk '{print $3}'`
echo "possible parentid: $parentid"
echo "possible zombieid: $zombieid"
echo "verifyzombie: $verifyzombie (this should be Z or similar)"
echo
# make sure this is a zombie file and we are not getting a Z from
# the command field of the ps -e -o ppid,pid,stat,command
if [ "$verifyzombie" == "Z" ] || [ "$verifyzombie" == "Zs" ] ; then
if [ "$parentid" != "$lastparentid" ] ; then
if [ "$lastparentid" != "0" ] ; then
echo "detach" >> /tmp/zombie_slayer.txt
fi
echo "attach $parentid" >> /tmp/zombie_slayer.txt
fi
# sometimes useful
echo "set unwindonsignal on" >> /tmp/zombie_slayer.txt
echo "call waitpid ($zombieid,0,0)" >> /tmp/zombie_slayer.txt
echo "Logging: Parent: $parentid Zombie: $zombieid"
lastparentid=$parentid
fi
#cat /tmp/zombie_slayer.txt
done
if [ "$lastparentid" != "0" ]
then
echo "detach" >> /tmp/zombie_slayer.txt
fi
if ! [ -f /tmp/zombie_slayer.txt ] ; then
echo "No zombies were located..."
exit 1
fi
cat /tmp/zombie_slayer.txt
read "Hit enter to Proceed..."
# Slay the zombies with gdb and the created command file
echo -e "\n\n"
echo "***********************************************************"
echo "Slaying zombie processes..."
echo "***********************************************************"
gdb -batch -x /tmp/zombie_slayer.txt
echo -e "\n\n"
echo "***********************************************************"
echo "Script complete."
echo "***********************************************************"
rm -f /tmp/zombie_slayer.txt
And make it executable
chmod +x /usr/local/bin/zombie-slayer
Start Slaying
When Zombie Slayer runs, it automatically seeks out and finds the zombie process.
It needs to have permissions to interact with the process and it’s parent process. So usually this means to run it as root or the user that owns the zombie. (Owning a zombie, now wouldn’t that be wonderful…)
Run the program:
zombie-slayer
The output should be something like this:
***********************************************************
This script will terminate all zombie process.
Press [ENTER] to continue or [CTRL] + C to cancel:
***********************************************************
***********************************************************
Creating command file...
***********************************************************
possible parentid: 1
possible zombieid: 3163
verifyzombie: Sl <-- not a zombie
possible parentid: 9259
possible zombieid: 9263
verifyzombie: S+ <-- not a zombie
possible parentid: 3954
possible zombieid: 3975
verifyzombie: Zs <-- a zombie
Logging: Parent: 3954 Zombie: 3975
attach 3954
call waitpid (3975,0,0)
***********************************************************
Slaying zombie processes...
***********************************************************
[[gdb output]]
$1 = 3975 <-- killed the zombie!
***********************************************************
Script complete."
***********************************************************
And with that, the zombie is gone.