web-dev-qa-db-fra.com

ProcessBuilder Java: Blocage du processus résultant

J'ai essayé d'utiliser ProcessBuilder de Java pour lancer une application sous Linux qui devrait fonctionner "à long terme". Ce programme est exécuté de manière à lancer une commande (dans ce cas, je lance une application de lecture multimédia), à lui permettre de s'exécuter et à vérifier qu'il ne s'est pas écrasé. Par exemple, vérifiez si le PID est toujours actif, puis relancez le processus s'il est mort.

Le problème que je me pose actuellement est que le PID reste actif dans le système, mais que l'interface graphique de l'application se bloque. J'ai essayé de déplacer ProcessBuilder (cmd) .start () dans un thread séparé, mais cela ne semble rien résoudre, comme je l'espérais.

Le résultat est essentiellement que, pour l'utilisateur, le programme APPEARS s'est écrasé, mais la suppression du processus Java qui pilote le ProcessBuilder.start () Process permet en fait au processus créé de reprendre son comportement normal. Cela signifie que quelque chose dans l'application Java interfère avec le processus engendré, mais je n'ai absolument aucune idée de quoi, à ce stade. (D'où la raison pour laquelle j'ai essayé de le séparer dans un autre thread qui ne semblait rien résoudre)

Si quelqu'un a des idées ou des suggestions à formuler, faites-le-moi savoir, car je ne peux pas, pour la vie, penser à la façon de résoudre ce problème.

Edit: Je n’ai aucune inquiétude quant au flux d’E/S créé à partir du Processus, et n’ai donc pris aucune mesure pour y remédier - cela pourrait-il provoquer un blocage du Processus lui-même?

30
Matt D

Si le processus écrit sur stderr ou stdout, et que vous ne le lisez pas, il se "bloque", bloquant lors de l'écriture sur stdout/err. Soit rediriger stdout/err vers/dev/null à l'aide d'un shell ou fusionner stdout/err avec redirectErrorStream (true) et créer un autre thread qui lit le stdout du processus

33
nos

Vous voulez le tour?

Ne démarrez pas votre processus depuis ProcessBuilder.start () . N'essayez pas de jouer avec la redirection/consommation de flux à partir de Java (surtout si vous ne donnez rien à ce sujet;)

Utilisez ProcessBuilder.start () pour démarrer un petit script Shell qui engloutit tous les flux d’entrée/sortie.

Quelque chose comme ca:

#!/bin/bash

Nohup $1 >/dev/null 2>error.log &

C'est-à-dire que si vous ne vous souciez pas de stdout et que vous voulez toujours vous connecter à stderr (et vous?) Dans un fichier ( error.log here).

Si vous ne vous souciez même pas de stderr, redirigez-le simplement vers stdout:

#!/bin/bash

Nohup $1 >/dev/null 2>1 &

Et vous appelez ce petit script à partir de Java, en lui donnant comme argument le nom du processus que vous voulez exécuter.

Si un processus exécuté sous Linux qui redirige stdout et stderr vers/dev/null produit toujours rien alors vous avez une installation Linux non conforme et non conforme;)

En d'autres mots: Just Works [TM] ci-dessus et éliminez le problème "vous devez consommer les flux dans ceci et dans cet ordre. Bla bla bla non-sens spécifique à Java" .

12
SyntaxT3rr0r

Le thread qui exécute le processus peut se bloquer s'il ne gère pas la sortie. Cela peut être fait en créant un nouveau thread qui lit le résultat du processus.

    final ProcessBuilder builder = new ProcessBuilder("script")
                    .redirectErrorStream(true)
                    .directory(workDirectory);

    final Process process = builder.start();
    final StringWriter writer = new StringWriter();

    new Thread(new Runnable() {
        public void run() {
            IOUtils.copy(process.getInputStream(), writer);
        }
    }).start();

    final int exitValue = process.waitFor();
    final String processOutput = writer.toString();
11
Hector

Je suis tombé sur ça après avoir eu un problème similaire. En accord avec nos, vous devez gérer la sortie. J'ai eu quelque chose comme ça:

ProcessBuilder myProc2 = new ProcessBuilder(command);
final Process process = myProc2.start();

et ça fonctionnait très bien. Le processus généré a même produit some output mais pas beaucoup. Lorsque j'ai commencé à produire beaucoup plus, il est apparu que mon processus n'était même plus lancé. J'ai mis à jour ceci:

ProcessBuilder myProc2 = new ProcessBuilder(command);
myProc2.redirectErrorStream(true);        
final Process process = myProc2.start();
InputStream myIS = process.getInputStream();
String tempOut = convertStreamToStr(myIS);

et ça a recommencé à fonctionner. (référez-vous à ce lien pour le code convertStreamToStr (): http://singztechmusings.wordpress.com/2011/06/21/getting-started-with-javas-processbuilder-a-sample-utility-class-to-interact -with-linux-from-Java-program/ )

5
user320781

JDK7 aura un support intégré pour la redirection E/S de sous-processus:

http://download.Oracle.com/javase/7/docs/api/Java/lang/ProcessBuilder.html

En attendant, si vous voulez vraiment supprimer stdout/stderr, il semble préférable (sous Linux) d'appeler ProcessBuilder avec une commande qui ressemble à:

["/bin/bash", "-c", "exec YOUR_COMMAND_HERE >/dev/null 2>&1"]
2
Martin Buchholz

Edit: Je n’ai aucune inquiétude quant au flux d’E/S créé à partir du Processus, et n’ai donc pris aucune mesure pour y remédier - cela pourrait-il provoquer un blocage du Processus lui-même?

Si vous ne lisez pas les flux de sortie créés par le processus, il est possible que l'application se bloque lorsque les mémoires tampons de l'application sont pleines. Je n'ai jamais vu cela se produire sous Linux (bien que je ne dis pas que ce n'est pas le cas), mais j'ai vu ce problème exact sous Windows. Je pense que cela est probablement lié.

2
Kaleb Pederson

Je crois que le problème est le tuyau de mise en mémoire tampon de Linux lui-même.

Essayez d'utiliser stdbuf avec votre exécutable

new ProcessBuilder().command("/usr/bin/stdbuf","-o0","*executable*","*arguments*");**

Le -o0 dit de ne pas mettre la sortie en mémoire tampon. Il en va de même pour -i0 et -e0 si vous souhaitez annuler la mémoire tampon de l'entrée et du canal d'erreur.

0
Danilo Souza

Au cas où vous auriez besoin de capturer stdout et stderr et de surveiller le processus, utiliser ensuite Apache Commons Exec m'a beaucoup aidé.

0
L.R.