2 Interprozeßkommunikation 2.1 JVM Ablaufumgebung 2.2 Java Native Interface (JNI) 2.2.1 Verwendung von C-Funktonen in Java Programmen 2.2.2 Zugriff auf Methoden von Java-Objekten in nativem Kode 2.2.3 Zugriff auf Attribute von Java-Objekten in nativem Kode 2.2.4 Ausnahmebehandlung 2.2.5 JNI in Multithread-Anwendungen 2.2.6 Aufruf der JVM in nativen Anwendungen Alois Schütte Advanced System Programming Interprozeßkommunikation: JVM Ablaufumgebung Der vom Betriebssystem initiierte Prozess, der durch den Aufruf eines JavaProgramms, erzeugt wird, hat eine Ablaufumgebung, auf die das JavaProgramm zugreifen kann. Der Zugriff erfolgt über Objekte der Klassen Process und Runtime, die von der JVM instanziiert werden. Die aktuelle Umgebung eines Java-Programms erhält man durch Aufruf der Methode getRuntime, die ein Process-Objekt zurück liefert Dieses Objekt hat Zugriff auf die Standard-Ein- und -Ausgabe und FehlerStröme der Ablaufumgebung. Beispiel: Java-Programm, das ein Betriebssystem-Kommando ausführt Alois Schütte Advanced System Programming Interprozeßkommunikation: JVM Ablaufumgebung 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. // Exec.java import java.io.*; public class Exec { public static void main(String args[]) { try { // create new process, exec command and print output // of command line by line String line; Process p = Runtime.getRuntime().exec(args); // new process BufferedReader in = // pipe with stdout new BufferedReader // from p (new InputStreamReader(p.getInputStream())); while ((line = in.readLine()) != null) { System.out.println(line); } in.close(); } catch (Exception err) { err.printStackTrace(); } } // main } Alois Schütte Advanced System Programming Interprozeßkommunikation: JVM Ablaufumgebung 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. // Exec.java import java.io.*; public class Exec { public static void main(String args[]) { try { // create new process, exec command and print output // of command line by line String line; Process p = Runtime.getRuntime().exec(args); // new process BufferedReader in = // pipe with stdout new BufferedReader // from p (new InputStreamReader(p.getInputStream())); while ((line = in.readLine()) != null) { System.out.println(line); } in.close(); $ java Exec ps -ao "pid ppid command" } PID PPID COMMAND catch (Exception err) { 771 685 login -pf as err.printStackTrace(); 772 771 -bash } 935 772 java Exec ps -ao pid ppid command } // main 936 935 /bin/ps -ao "pid ppid command" $ } Alois Schütte Advanced System Programming Interprozeßkommunikation: JVM Ablaufumgebung Genauso, wie das Java-Programm lesend mit einem erzeugten Prozess kommunizieren kann, ist es auch möglich, die Standardeingabe eines erzeugten Prozesses mit Daten zu bestücken. Beispiel: Arithmetischen Ausdruck vom Programmaufruf senden an bc, das den Ausdruck auswertet und das Ergebnis wieder an das Java-Programm mit dem o.a. Mechanismus zurückgibt. Verwendet wird das Gegenstück zu getInputStream(), die Methode getOutputStream(), um dem Prozess über seine Standard-Eingabe Daten zu senden. $ java Bc (0.8+0.2)*5*2 10 $ Alois Schütte Advanced System Programming Interprozeßkommunikation: JVM Ablaufumgebung 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. // Bc.java import java.io.*; public class Bc { public static void main(String args[]) { try { // create new process, exec bc and print output String line; Process p = Runtime.getRuntime().exec("bc"); // new process BufferedReader in = // pipe with new BufferedReader // stdout from p (new InputStreamReader(p.getInputStream())); BufferedWriter out = // pipe with new BufferedWriter // stdin from p (new OutputStreamWriter(p.getOutputStream())); out.write("scale=2\n"); // some inits for bc out.write(args[0],0, args[0].length()); // expression out.write(’\n'); // bc starts eval out.flush(); out.close(); // bc is waiting to read from pipe // program would hang iff not closed // because in.readLine could not terminate while ((line = in.readLine()) != null) { $ java Bc (0.8+0.2)*5*2 System.out.println(line); 10 } $ in.close(); } catch (Exception err) {err.printStackTrace(); } } // main } Alois Schütte Advanced System Programming Interprozeßkommunikation: JVM Ablaufumgebung In Unix wird jedem Prozess ein Exit-Status zugeordnet, der Auskunft über die Art der Termination des Prozesses gibt. Der Integer-Wert Null bedeutet dabei normalerweise, dass ein Prozess fehlerfrei terminiert, ein von Null verschiedener Wert drückt eine Beendigung im Fehlerfall aus. Ein Java-Programm kann auf den Exit-Status des von ihm erzeugten Prozesses mit der Methode exitValue der Klasse Process zugreifen. Eine weitere Methode dieser Klasse (destroy) erlaubt es, einen erzeugten Prozess zu beenden. Ein Prozess hat eine Ablaufumgebung, in der Umgebungsvariable Werte annehmen können. Z.B. zeigt die Variable HOME in Unix auf das StandardVerzeichnis eines angemeldeten Benutzers. Durch das Unix-Kommando env kann man sich die Umgebungsvariablen anzeigen lassen. Beispiel: Umgebung des erzeugten Prozesses bestimmen Alois Schütte Advanced System Programming Interprozeßkommunikation: JVM Ablaufumgebung 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. // Exec1.java import java.io.*; public class Exec1 { public static void main(String args[]) { StringBuffer cmdLine = new StringBuffer(args[0]); for (int i=1; i<args.length; i++) cmdLine = cmdLine.append(" ").append(args[i]); try { // create new process, exec command and print output String line; // Environment for new process String[] env = {"NAME=as", new String("HOST=hal")}; Process p = Runtime.getRuntime().exec(cmdLine.toString(), env); // Environment BufferedReader input = new BufferedReader (new InputStreamReader(p.getInputStream())); while ((line = input.readLine()) != null) { System.out.println(line); } input.close(); if (p.exitValue() != 0) // process terminated normaly? System.out.println("Command terminated with exit code " + p.exitValue()); } catch (Exception err) {} $ java Exec1 env } NAME=as HOST=hal } $ java Exec1 cat DateiExistiertNicht Command terminated with exit code 1 $ Alois Schütte Advanced System Programming Interprozeßkommunikation: JVM Ablaufumgebung Wenn ein Java-Programm ausgeführt wird, so wird es von der JVM in einem Betriebssystemprozess verarbeitet. Jeder Prozess kann asynchron eintretende Ereignisse empfangen. Diese werden je nach Betriebsystem unterschiedlich behandelt. In Unix reagiert ein Prozess auf ein eintreffendes Signal mit einem je nach Signal spezifizierten Default-Verhalten. Will man das Verhalten per Programm beeinflussen, muss man einen SignalHandler schreiben, der den gewünschten Kode ausführt, wenn ein Signal eintrifft. Die Behandlung von Signalen im JVM-Prozess erfolgt mehrstufig. Wenn der JVM-Prozess ein Signal empfängt, wird zunächst geprüft, ob es für diesen Prozess eine eigene Signalbehandlungsroutine gibt. Ist dies der Fall, wird das Signal durch ihn behandelt, ansonsten wird es an die JVM weitergereicht. Damit ein Signal von der JVM mit einem in Java geschriebenen Signal-Handler abgefangen werden kann, kann die Klasse sun.misc.Signal verwendet werden. Eine Java Signalbehandlung unterscheidet sich nicht viel von einem Signal-Handler der z.B. in C realisiert ist. Der Unterschied ist, dass ein eintreffendes Signal bewirkt, dass die JVM einen neuen Thread erzeugt, der die Signalbehandlung durchführt. Beispiel: Abfangen von TERM Alois Schütte Advanced System Programming Interprozeßkommunikation: JVM Ablaufumgebung 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. // SignalDemo.java import sun.misc.Signal; import sun.misc.SignalHandler; class SignalDemo { public static void main(String[] args) { try { // Install signal handler Handler h = new Handler("TERM"); // 15 while (true) { System.out.println("main"); Thread.sleep(1000); } } catch (Exception e) { System.out.println("AppWrap exception "+e); } } } class Handler implements SignalHandler { Handler (String signalName) { Signal signal = new Signal(signalName); Signal.handle(signal,this); // handle signal } // Signal handler method public void handle(Signal sig) { System.out.println("Signal handler called for signal "+sig); } } Alois Schütte Advanced System Programming Interprozeßkommunikation: JNI Alois Schütte Advanced System Programming