How to Kill a Linux Process in Java with Sigkill Process.Destroy() Does Sigterm

how can I kill a Linux process in java with SIGKILL Process.destroy() does SIGTERM

Not using pure Java.

Your simplest alternative is to use Runtime.exec() to run a kill -9 <pid> command as an external process.

Unfortunately, it is not that simple to get hold of the PID. You will either need to use reflection black-magic to access the private int pid field, or mess around with the output from the ps command.

UPDATE - actually, there is another way. Create a little utility (C program, shell script, whatever) that will run the real external application. Code the utility so that it remembers the PID of the child process, and sets up a signal handler for SIGTERM that will SIGKILL the child process.

Program a unix kill in Java

In short, no, there isn't.

On a unix system, the best bet is to use Runtime.exec().

kill process using sigterm and escalate to sigkill after timeout

There's the timeout command, which allows you to cap a process' execution time and escalate to a SIGKILL if it doesn't respond promptly to the initial signal (SIGTERM by default). This isn't quite what you're asking for, but it might be sufficient.

To do what you're actually describing (send a signal, briefly await, then send a kill) you may have to do a bit of bookkeeping yourself, as this question details.

One option would be to use Upstart (or I imagine other service managers), which provides a kill timeout n command that does what you want.


As an aside, many systems would treat 30 minutes as much too long to wait for SIGTERM. Linux does something akin to what you're describing on shutdown, for instance, but gives processes barely a few seconds to clean up and exit before SIGKILLing them. For other use cases you certainly can have a long-lived termination like you describe (e.g. with Upstart), but YMMV.

Killing a Java Process object

The solution in my case, since I was writing both the parent and the child processes, was to add a ShutdownHook which halted the JVM (a simple System.exit(1) was not sufficient):

Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
Runtime.getRuntime().halt(5);
}
});

After this, when a SIGTERM was received the process would terminate, as though it had been sent SIGKILL by the parent process.

How to gracefully handle the SIGKILL signal in Java

It is impossible for any program, in any language, to handle a SIGKILL. This is so it is always possible to terminate a program, even if the program is buggy or malicious. But SIGKILL is not the only means for terminating a program. The other is to use a SIGTERM. Programs can handle that signal. The program should handle the signal by doing a controlled, but rapid, shutdown. When a computer shuts down, the final stage of the shutdown process sends every remaining process a SIGTERM, gives those processes a few seconds grace, then sends them a SIGKILL.

The way to handle this for anything other than kill -9 would be to register a shutdown hook. If you can use (SIGTERM) kill -15 the shutdown hook will work. (SIGINT) kill -2 DOES cause the program to gracefully exit and run the shutdown hooks.

Registers a new virtual-machine shutdown hook.

The Java virtual machine shuts down in response to two kinds of events:

  • The program exits normally, when the last non-daemon thread exits or when the exit (equivalently, System.exit) method is invoked, or
  • The virtual machine is terminated in response to a user interrupt, such as typing ^C, or a system-wide event, such as user logoff or system shutdown.

I tried the following test program on OSX 10.6.3 and on kill -9 it did NOT run the shutdown hook, as expected. On a kill -15 it DOES run the shutdown hook every time.

public class TestShutdownHook
{
public static void main(String[] args) throws InterruptedException
{
Runtime.getRuntime().addShutdownHook(new Thread()
{
@Override
public void run()
{
System.out.println("Shutdown hook ran!");
}
});

while (true)
{
Thread.sleep(1000);
}
}
}

There isn't any way to really gracefully handle a kill -9 in any program.

In rare circumstances the virtual
machine may abort, that is, stop
running without shutting down cleanly.
This occurs when the virtual machine
is terminated externally, for example
with the SIGKILL signal on Unix or the
TerminateProcess call on Microsoft
Windows.

The only real option to handle a kill -9 is to have another watcher program watch for your main program to go away or use a wrapper script. You could do with this with a shell script that polled the ps command looking for your program in the list and act accordingly when it disappeared.

#!/usr/bin/env bash

java TestShutdownHook
wait
# notify your other app that you quit
echo "TestShutdownHook quit"

How to send SIGINT signal from Java to an external process?

Are you running the external program as a java.lang.Process? As the Process class has a destroy() method.

How to stop a command being executed after 4-5 seconds through process builder?

As I understand it you want to stop a subprocess if it runs longer than four or five seconds. This cannot be done directly with ProcessBuilder (you can see that no relevant method exists in the class), but you can implement this behavior easily enough once the subprocess has begun.

Calling Process.waitFor() as you do in your sample code is problematic because it will block your current thread indefinitely - if your process takes longer than five seconds .waitFor() will not stop it. However .waitFor() is overloaded and its sibling takes a timeout argument.

public boolean waitFor(long timeout, TimeUnit unit) throws InterruptedException

Causes the current thread to wait, if necessary, until the subprocess represented by this Process object has terminated, or the specified waiting time elapses.

You can use this in tandem with Process.destroy() to stop the process if it takes too long. For example:

Process process = new ProcessBuilder(command, and, arguments)
.redirectErrorStream(true)
.directory(workingDir)
.start();

process.waitFor(5, TimeUnit.SECONDS);
process.destroy();
process.waitFor(); // wait for the process to terminate

This relies on the fact that Process.destroy() is a no-op when called on an already-finished subprocess. Before Java 9 this behavior was not documented, but in practice has always been the case. The alternative would be to inspect the return value of .waitFor(), but this would introduce a TOCTTOU race.

What about Process.destroyForcibly()? Generally speaking you should not call this method (another thing the JDK could be clearer about), however if a process is truly hung it may become necessary. Ideally you should ensure your subprocesses are well-behaved, but if you must use .destroyForcibly() this is how I would recommend doing so:

// Option 2
process.waitFor(5, TimeUnit.SECONDS); // let the process run for 5 seconds
process.destroy(); // tell the process to stop
process.waitFor(10, TimeUnit.SECONDS); // give it a chance to stop
process.destroyForcibly(); // tell the OS to kill the process
process.waitFor(); // the process is now dead

This ensures that misbehaving processes will be killed promptly, while still giving properly implemented programs time to exit upon being instructed. The exact behavior of .destroy() and .destroyForcibly() is OS-specific, but on Linux we can see that they correspond to SIGTERM and SIGKILL:

int sig = (force == JNI_TRUE) ? SIGKILL : SIGTERM;
kill(pid, sig);

You should rarely have a need to call .destroyForcibly(), and I would suggest only adding it if you discover it is necessary.

Option 2 is conceptually similar to using the timeout command like so:

$ timeout --kill-after=10 5 your_command

It's easy enough to replicate Process.waitFor(long, TimeUnit) in Java 7, there's nothing magic about the default Java 8 implementation:

public boolean waitFor(long timeout, TimeUnit unit)
throws InterruptedException
{
long startTime = System.nanoTime();
long rem = unit.toNanos(timeout);

do {
try {
exitValue();
return true;
} catch(IllegalThreadStateException ex) {
if (rem > 0)
Thread.sleep(
Math.min(TimeUnit.NANOSECONDS.toMillis(rem) + 1, 100));
}
rem = unit.toNanos(timeout) - (System.nanoTime() - startTime);
} while (rem > 0);
return false;
}


Related Topics



Leave a reply



Submit