Wednesday, May 30, 2012

Best way to kill all child processes


I basically want to kill a whole process tree. What is the best way to do this using any common scripting languages. I am looking for a simple solution.



Source: Tips4all

14 comments:

  1. You don't say if the tree you want to kill is a single process group. (This is often the case if the tree is the result of forking from a server start or a shell command line.) You can discover process groups using GNU ps as follows:

    ps x -o "%p %r %y %x %c "


    If it is a process group you want to kill, just use the kill(1) command but instead of giving it a process number, give it the negation of the group number. For example to kill every process in group 5112, use kill -TERM -5112.

    ReplyDelete
  2. pkill -TERM -P 27888


    where 27888 is parent's PID.

    Or more robust:

    CPIDS=$(pgrep -P 27888); (sleep 33 && kill -KILL $CPIDS &); kill -TERM $CPIDS


    which schedule killing 33 second later and politely ask processes to terminate.

    ReplyDelete
  3. To kill a process tree recursively, use killtree.sh:

    #!/bin/bash

    killtree() {
    local _pid=$1
    local _sig=${2-TERM}
    for _child in $(ps -o pid --no-headers --ppid ${_pid}); do
    killtree ${_child} ${_sig}
    done
    kill -${_sig} ${_pid}
    }

    if [ $# -eq 0 -o $# -gt 2 ]; then
    echo "Usage: $(basename $0) <pid> [signal]"
    exit 1
    fi

    killtree $@

    ReplyDelete
  4. brad's answer is what I'd recomment too, except that you can do away with awk altogether if you use the --ppid option to ps.


    for child in $(ps -o pid -ax --ppid $PPID)
    do
    .......
    done

    ReplyDelete
  5. if you know pass the pid of the parent process, here's a shell script that should work:

    for child in $(ps -o pid,ppid -ax | \
    awk "{ if ( \$2 == $pid ) { print \$1 }}")
    do
    echo "Killing child process $child because ppid = $pid"
    kill $child
    done

    ReplyDelete
  6. I use a little bit modified version of a method described here:
    http://stackoverflow.com/a/5311362/563175

    So it looks like that:

    kill `pstree -p 24901 | sed 's/(/\n(/g' | grep '(' | sed 's/(\(.*\)).*/\1/' | tr "\n" " "`


    where 24901 is parent's PID.

    ReplyDelete
  7. Since you say you can use any common scripting languages, you could take a look at this question: How can I kill a whole process tree with Perl?

    ReplyDelete
  8. Thanks for your wisdom, folks. My script was leaving some child processes on exit and the negation tip made things easier. I wrote this function to be used in other scripts if necessary:

    # kill my group's subprocesses: killGroup
    # kill also myself: killGroup -x
    # kill another group's subprocesses: killGroup N
    # kill that group all: killGroup -x N
    # N: PID of the main process (= process group ID).

    function killGroup () {
    local prid mainpid
    case $1 in
    -x) [ -n "$2" ] && kill -9 -$2 || kill -9 -$$ ;;
    "") mainpid=$$ ;;
    *) mainpid=$1 ;;
    esac
    prid=$(ps ax -o pid,pgid | grep $mainpid)
    prid=${prid//$mainpid/}
    kill -9 $prid 2>/dev/null
    return
    }


    Cheers.

    ReplyDelete
  9. To add to Norman Ramsey's answer, it may be worth looking at at setsid if you want to create a process group.
    http://pubs.opengroup.org/onlinepubs/009695399/functions/setsid.html


    The setsid() function shall create a
    new session, if the calling process is
    not a process group leader. Upon
    return the calling process shall be
    the session leader of this new
    session, shall be the process group
    leader of a new process group, and
    shall have no controlling terminal.
    The process group ID of the calling
    process shall be set equal to the
    process ID of the calling process. The
    calling process shall be the only
    process in the new process group and
    the only process in the new session.


    Which I take to mean that you can create a group from the starting process. I used this in php in order to be able to kill a whole process tree after starting it.

    This may be a bad idea. I'd be interested in comments.

    ReplyDelete
  10. ps -o pid= --ppid $PPID | xargs kill -9

    ReplyDelete
  11. if you have pstree and perl on your system, you can try this:

    perl -e 'kill 9, (`pstree -p PID` =~ m/\((\d+)\)/sg)'

    ReplyDelete
  12. It is probably better to kill the parent before the children; otherwise the parent may likely spawn new children again before he is killed himself. These will survive the killing.

    My version of ps is different from that above; maybe too old, therefore the strange grepping...

    To use a shell script instead of a shell function has many advantages...

    However, it is basically zhigangs idea



    #!/bin/bash
    if test $# -lt 1 ; then
    echo >&2 "usage: kiltree pid (sig)"
    fi ;

    _pid=$1
    _sig=${2:-TERM}
    _children=$(ps j | grep "^[ ]*${_pid} " | cut -c 7-11) ;
    echo >&2 kill -${_sig} ${_pid}
    kill -${_sig} ${_pid}
    for _child in ${_children}; do
    killtree ${_child} ${_sig}
    done

    ReplyDelete
  13. If you know the pid of the thing you want to kill, you can usually go from the session id, and everything in the same session. I'd double check, but I used this for scripts starting rsyncs in loops that I want to die, and not start another (because of the loop) as it would if I'd just killall'd rsync.

    kill $(ps -o pid= -s $(ps -o sess --no-heading --pid 21709))


    If you don't know the pid you can still nest more

    kill $(ps -o pid= -s $(ps -o sess --no-heading --pid $(pgrep rsync )))

    ReplyDelete
  14. This is my version of killing all the child processes using bash script.
    It does not use recursion and depends on pgrep command.

    Use

    killtree.sh PID SIGNAL


    Contents of killtrees.sh

    #!/bin/bash
    PID=$1
    if [ -z $PID ];
    then
    echo "No pid specified"
    fi

    PPLIST=$PID
    CHILD_LIST=`pgrep -P $PPLIST -d,`

    while [ ! -z "$CHILD_LIST" ]
    do
    PPLIST="$PPLIST,$CHILD_LIST"
    CHILD_LIST=`pgrep -P $CHILD_LIST -d,`
    done

    SIGNAL=$2

    if [ -z $SIGNAL ]
    then
    SIGNAL="TERM"
    fi
    #do substring from comma to space
    kill -$SIGNAL ${PPLIST//,/ }

    ReplyDelete