How to Kill a Whole Process Tree with Perl

Perl: kill whole tree from any child

When the shell starts a process, it makes it the start of a process group.

$ perl -MPOSIX -E'
my $pid = fork();
say getpgrp(), " ", getpid();
waitpid($pid, 0) if $pid;
'
6164 6164
6164 6167

If none of the children have their process group altered, you could send a signal to the process group. For example, the following will send SIGTERM to all process in the same process group as process 1234:

kill -s TERM -1234

If some of the children have their process group altered, you will need to throw a catchable signal (e.g. not SIGKILL), and the parents of the children with altered process groups will need to explicitly catch and rethrow the signal to those children.

Best way to kill process itself and all child processesg

First off - don't kill -9. That's bad practice unless you absolutely have to. SIGTERM or -15 is much nicer.

To send things to your process group, the easiest approach is (from perlipc: http://perldoc.perl.org/perlipc.html#Signals)

kill -$$

Sending a signal to a negative process ID means that you send the signal to the entire Unix process group.

Alternatively - fork returns a pid. You can explicitly kill that pid.

If you're having problems with children not exiting in response to kill signals though, then the most common problem is that they've entered an uninterruptible state.

This might be something like IO on an NFS mount (or other IO to device that's unavailable). Unfortunately this is an OS specific problem, which isn't solvable via perl. You can check this by running ps -e v and check for the 'state' flag.

state    The state is given by a sequence of characters, for example, "RWNA". The      first character indicates the run state of the process:
D Marks a process in disk (or other short term, uninterruptible) wait.
I Marks a process that is idle (sleeping for longer than about 20 seconds).
L Marks a process that is waiting to acquire a lock.
R Marks a runnable process.
S Marks a process that is sleeping for less than about 20 seconds.
T Marks a stopped process.
W Marks an idle interrupt thread.
Z Marks a dead process (a "zombie").

Either that or they've become zombie processes, and are waiting to be reaped. You can avoid this by setting $SIG{'CHLD'} = "IGNORE";
But as noted in the comments - you shouldn't do this as well as killing by fork-pid, because of the race condition it creates.

You should also note - if you configure a signal handler, and then fork, then the forked child also has that handler. You may need to remove it again in the child process.

What's the best way to send a signal to all members of a process group?

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.

Killing an child process run through system() function in infinte loop is not working in perl

$pid is the process id of the child process that was created by the fork call. It is a perl process. When you call system($command), that creates a new process, running the command $command, and you don't know what the process id of that command is. Calling kill('KILL',$pid) sends a signal to the background perl process, but not necessarily to any of its children, so you can't be sure that the system call will also receive the signal.

One solution is to use exec instead of system.

...
} elsif ($pid == 0) {
print "Printed by child process\n";
exec("./loop.bin");
} else {
...

exec is like system, but instead of launching the command in a new process, it replaces the existing process with a process running the command, preserving the process id. So after calling exec, the child perl process will be gone, and $pid will refer to the process running the command ./loop.bin. Now you can signal $pid and it will be sent to the desired process.

How to terminate a background while loop in Linux?

What may be happening is that you are killing the perl program, but the loop throwing them off is still running. You need to kill the entire login tree.

ps -f -u$USER (or something like it, depends on the OS) shows the process id, parent process id, and tty of your processes.

Kill the process and the parent, all the way up the process tree to your unwanted shell running the loop. Notice the tty of the offending process... if different from your current tty kill all processes on that tty; you may need a new terminal window to get a different tty.

Alternate: Linux has ps xjf to show the processes in tree format... start with the relevant shell process and kill that and all child processes.

Extra info: processes that don't go away under kill -9, keeping the same PID (not your condition), are likely "zombie" processes that are waiting for their parent to read their exit status. Zombies have actually stopped and gone away and are not alive so you can't kill them. Only a ghost remains in the process table waiting for the parent to read the pending exit status. Killing the parent will reassign the process to init, which will read the status.

In C#, how to kill a process tree reliably

Use ManagmentObjectSearcher and a little recursion:

private static void KillProcessAndChildren(int pid)
{
ManagementObjectSearcher searcher = new ManagementObjectSearcher
("Select * From Win32_Process Where ParentProcessID=" + pid);
ManagementObjectCollection moc = searcher.Get();
foreach (ManagementObject mo in moc)
{
KillProcessAndChildren(Convert.ToInt32(mo["ProcessID"]));
}
try
{
Process proc = Process.GetProcessById(pid);
proc.Kill();
}
catch (ArgumentException)
{
// Process already exited.
}
}


Related Topics



Leave a reply



Submit