Link to home
Start Free TrialLog in
Avatar of tikiliainen
tikiliainen

asked on

Killing a java.lang.Process and its children

This is a general question about java.lang.Process and the semantics of java.lang.Process.destroy( ) - particularly under *nix (Solaris + Linux). Windows-related knowledge is also welcome but I am primarily concerned about the signals sent to the process when destroy( ) is called.

I have done a relatively quick google on the matter but could not find what the signal sent to the process is -- so I assume it's SIGTERM (and not SIGKILL). Is there a way to specify a signal that I want, or is this not in the standard Java distribution because this is platform-dependent? Anyone who could point answer this and point me to an "official" resource where this is described stands a good chance of getting the points :-)

However, for complete glory, I would also like to know how to kill (i.e. the equivalent of "kill -9" -- sending SIGKILL) a process spawned from a Java program _and_ all of its children. I am not sure whether sending SIGKILL also ensures that all the child processes will die too. I would like to avoid having to shell out to a script that would ps or pstree the situation, perform some awking and then kill -9 the children. Is there a way to kill the children, do I need to do it or does this happen automatically?

Cheers.
Avatar of Mayank S
Mayank S
Flag of India image

As far as I know, what Process.destroy() actually does is highly dependant on the OS, the VM and its implementation of the abstract 'Process' class returned from Runtime.exec ().
>> so I assume it's SIGTERM

I guess that's correct.

>> there a way to specify a signal that I want, or is this not in the standard Java distribution because this is platform-dependent?

You might not be able to specify the signal. The VM's implementation will decide what command to send, depending upon the platform you're working on, because every platform will have its own signals and the VM (which will be for the specific platform) will make use of them.

Perhaps this is also why the destroy () method in the Process class is abstract. Based on the process you invoke from Runtime.exec (), an appropriate instance is made by the VM (depending on the OS) and returned.

Calling process.destroy () however, might not kill all child processes. Maybe that if you do a Ctrl-C, it will kill the process and all sub-processes, but I don't know how to simulate that in the program. Will try to look for something on killing sub-processes as well.
As an alternative, perhaps you could use waitFor () and make every process wait till its sub-processes end?
Irrespective of what platform you are on, I guess there is no standard API to extract the native OS process identifier to implement your own clean-up.

Examing the implementation of Runtime.exec()/Process shows that all of the important functions are in native methods, for which the source is not provided so it's difficult to work out what the implementations actually do, or attempt to do.

If the child processes are not dying, then it might be necessary to implement your own (native) Runtime.exec()/ Process replacement to perform the relevant process killing.
Avatar of tikiliainen
tikiliainen

ASKER

Pressing Ctrl-C is equivalent to sending a SIGTERM. As far as waitFor() goes, I use it to wait for :-) the process I spawned to end -- I think waiting for the children to end is implicit, but I might be wrong. In fact, I do not think it is possible to actually get any reference to the children at all. I suppose the question really is "does the SIG(TERM|KILL) get sent to all the children too?"
>> I suppose the question really is "does the SIG(TERM|KILL) get sent to all the children too?"

Perhaps not, if your children processes are not getting killed. It might depend on the platform as well. Maybe that in some cases, it does get sent to the child processes as well. But if it does not, then you might have to put your own native implementation for Runtime.exec ()/ Process, depending on the platform.
I am fairly certain it is a SIGKILL.

This may be a job for JNI. I battled this type of problem for awhile.
The semantics are highly OS dependent.

One idea to test this is to write a program (not necessarly Java) that installs a handler to catch SIGTERM and print a message in that handler and exit. Spawn the process, call destroy() on the Process object, and view the signal output.
If you see your message from the handler, you know SIGTERM was called.
If you do not see your message from the SIGTERM handler, then SIGKILL was called.

If you want to be certain of the semantics of the child process here is a dirty trick.
The unix subclass java.lang.Process is "java.lang.UNIXProcess"
There is a private field called,
   private int pid;
Using native code or reflection you can circumvent the "private" access modifier.
and fetch the pid field. Once you have the pid you could call into JNI and use the kill() system call, sending a signal of your choice to that process.

Now, what I just suggested is a very naughty and unclean solution. I acknowledge that and
hope I don't hurt anyone's feelings with it. The code is VERY unportable and unstable since it depends on information that it shouldn't (a private field on a hidden class). It might not even work on Java 1.5 and certainly only works on Linux/Unix machines.

Happy hacking,
- Tim
Thanks Tim,

This is very close to what I needed to know. I am amused very much by the idea you suggest for getting the PID. I am however, unable to find any refernce to java.lang.UNIXProcess anywhere, apart from a very 1.1-like API page. I assume UNIXProcess was deprecated and removed.

How did you find the code for it?
SOLUTION
Avatar of Mayank S
Mayank S
Flag of India image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks a lot, Tim. I am very puzzled though, why Unix process would not be documented *anywhere on the net*. It is not a public class (as javap shows), so I guess this explains why it's not in *any* of the standard Javadocs for recent JDKs.

My ultimate aim (as far as this question goes) is to be able to get to all the child processes of the one I spawn. I am guessing this is impossible without going through JNI, which I really do not want to do because that will give me a headache (2 languages + bindings, on 2 OS's).

One thing which is really strange is the following:

mtikilia@machine1[808]:~>javap java.lang.UNIXProcess
Compiled from UNIXProcess.java
class java.lang.UNIXProcess extends java.lang.Process {
    java.lang.UNIXProcess(java.lang.String[],java.lang.String[]) throws java.io.IOException;
    static java.io.FileDescriptor access$0(java.lang.UNIXProcess);
    static java.io.OutputStream access$1(java.lang.UNIXProcess);
    static int access$10(java.lang.UNIXProcess, int);
    static boolean access$11(java.lang.UNIXProcess);
    static void access$12(java.lang.UNIXProcess, boolean);
    static int access$13(java.lang.UNIXProcess);
    static void access$14(java.lang.UNIXProcess, int);
    static void access$2(java.lang.UNIXProcess, java.io.OutputStream);
    static java.io.FileDescriptor access$3(java.lang.UNIXProcess);
    static java.io.InputStream access$4(java.lang.UNIXProcess);
    static void access$5(java.lang.UNIXProcess, java.io.InputStream);
    static java.io.FileDescriptor access$6(java.lang.UNIXProcess);
    static java.io.InputStream access$7(java.lang.UNIXProcess);
    static void access$8(java.lang.UNIXProcess, java.io.InputStream);
    static int access$9(java.lang.UNIXProcess);
    public void destroy();
    public synchronized int exitValue();
    public java.io.InputStream getErrorStream();
    public java.io.InputStream getInputStream();
    public java.io.OutputStream getOutputStream();
    public synchronized int waitFor() throws java.lang.InterruptedException;
}
mtikilia@machine1[809]:~>which java
/usr/bin/java
mtikilia@machine1[810]:~>ls -l /usr/bin/java
lrwxrwxrwx   1 root     other         16 Oct 22  2002 /usr/bin/java -> ../java/bin/java
mtikilia@machine1[811]:~>jar -tf /usr/bin/../java/src.jar | grep Process
src/java/lang/Process.java
mtikilia@machine1[812]:~>

javap clearly shows that the class is compiled from a UNIXProcess.java, but it is nowhere to be found in the src.jar! Mystery! Do you know where it lives?
Even I didn't find it in the documentation, that's why I said that its probably been removed.

Since its a platform-specific class (does stuff only for the UNIX platform), perhaps its not there in the sources because they contain only the public classes used by all platforms. Also, I guess this one has been given friendly access (and not public) because its more used internally by the JVM and only in the classes of the same package.
>perhaps its not there in the sources because they contain only the public
>classes used by all platforms.

Are you double sure this is true? A quick check seems to agree with you -- i.e. only platform independent classes are present in src.jar, but this clearly does not make sense, as you need the platform specific ones to compile the Java distribution on a specific platform.
>> Are you double sure this is true?

I'm not sure. That's why I said: 'perhaps'.

>> but this clearly does not make sense

Depends upon Sun. Maybe that they did not want to give you the sources for it. And it was easier to provide the same src.jar for all platforms.
The detective work I did to find it was not difficult, but vnot very good at answering our question.

You will see this pattern in Java alot. They give the interface as an abstract class
"java.lang.Process", then do all the implementation dependent stuff in a concrete
OS dependent sublcass.

You will see this pattern in java.awt.Graphics (subclass depends on the graphics environment) too and many other different classes. DirectByteBuffer has man different system dependent version.

A cleaner solution might be to write a simple platform specific class that gives you the functionality you need.
You could even extend Process (although I would not without a better reason).
Good luck,
- T
>> although I would not without a better reason

But perhaps it is the best way out ;-)
I have also done a little detective work, however, in a different direction: if it is not possible to get to the children, how can we make sure the parent will kill them before dying (you know, Mohammed, mountain).

This also gave a precise answer to exactly what signal gets sent to the process by Process.destroy() on Solaris. The answer is SIGTERM.

I was trying to find out whether there was a way of passing the signal along to the chilren here: https://www.experts-exchange.com/questions/20937086/Why-do-script-children-refuse-to-die.html
and as you can see, the answer was similar to what was suggested in this thread: write a signal handler for SIGTERM and use that to kill the children.

So, just trying my luck, I wrote a similar script with a child process and a SIGTERM handler and ran it from Java. I then Process.destroy()'d it from Java and the handler fired! To double-check, I changed the signal trapped by the script from SIGTERM to SIGHUP and repeated. When the parent was killed from Java, the handler did not fire, thus confirming that the signal sent to the process by Process.destroy is definitely SIGTERM. This actually makes more sense than sending SIGKILL as the killed process is given a chance to clean up.

So, to draw a line under this: you cannot kill the children from Java without some horrible (and adventurous!) hacking and shelling out or native code calling. However, it is possible to get around this by using signal handlers at script-level (or program level).