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.

Zombie

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

zombie

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.