MasterGeekMX
MasterGeekMX

Reputation: 63

Trim a binary process tree level by level starting from the leafs

I neeed to do a bash script that runs a C program that spawns an n depth binary tree of processes using fork(), then trims it level by level starting from the leaves of the tree up to the root.

The C code that makes the process tree is quite simple. You can see the full code here, but a TL;DR version is as follows:

void tree(int n):
   if (n==0) exit
   rchild=fork()
   if (parent)
      lchild=fork()
      if (left child)
         tree(n-1)
   else //right child
      tree(n-1)
sleep(1000)

looking at pstree -pn PID output I noticed that the PIDs of the childs were consecutive, so I decided to kill the process by numeric value based upon the PID of the first instance. But maybe becasue of the commands taking up PID values or another system processes, running the code inside the script knocks off the values of the PIDs, so my approach no longer works. I'm trying to take the PIDs directly by using the output of pstree, but for now it seems that it will be a mess of sed and awk calls just to get one level of PIDs.

here is the bash script that I have so far:

#!/bin/bash

if [ ! -f "./fork.run" ]
then
    >&2 echo 'Error: binary mising'
    >&2 echo 'Generate it with "gcc -o fork.run fork.c"'
    exit
fi

if [ "$1" == "" ]
then
    >&2 echo "Error: you need to indicate the depth of the tree"
    exit
fi

./fork.run $(( $1 - 1 )) &
fork=$! #store tree's PID
n=$1

echo "fork started with PID $fork"


while [ $n -gt 1 ]
do

    echo "Tree of the process $fork:"
    pstree -pn $fork

    sleep 1

    #calculation based on properties of the binary tree
    min=$(( 2 ** (n - 1) + $fork ))
    max=$(( (2 ** n) - 1 + $fork ))

    echo "starting trim: level $n"


    for i in `seq $min $max`
    do
        echo "killing $i"
        kill $i #I also tried kill -9 $i, but it's the same.
    done

    sleep 1

    echo "processes from $min to $max trimmed"

    let n--
done

pstree -pn $fork

sleep 1

echo "starting trim: level $n (final)"

kill $fork

sleep 1

echo "initial process ($fork) trimmed"

Upvotes: 0

Views: 242

Answers (1)

Armali
Armali

Reputation: 19395

it seems that it will be a mess of sed and awk calls just to get one level of PIDs.

Well, it's not all too messy with awk. The following replacement of your while loop processes the pstree output with an awk script which determines (on the basis of the position in each line) the tree levels to which the contained processes belong and kills the ones on the selected level n.

while [ $n -gt 1 ]
do
    echo "Tree of the process $fork:"
    pstree -p $fork
    sleep 1
    echo "starting trim: level $n"
    pstree -pl $fork |
    gawk -vn=$n '
    /^[^ ]/ { level = 1; start[1] = 1 }             # line starting with non-space has top level
    /^ /    { level = trats[match($0, "[|`]-")] }   # determine first level of line from position of |- or `-
    {   while (i = match(substr($0, start[level]), ")-[-+]-"))
        { start[level+1] = start[level]+i+1; ++level; trats[start[level]] = level } # hash level by position
        if (match(substr($0, start[n]), "^.-[^(]+.([0-9]+)", p)) { print "killing "p[1]; system("kill "p[1]) }
    }   '
    sleep 1
    echo "processes trimmed"
    let n--
done

Upvotes: 1

Related Questions