2013-06-07 27 views
9

So che se si utilizza ProcessBuilder.start in Java per avviare un processo esterno si deve dovere consumare il suo stdout/stderr (ad esempio vedere here). In caso contrario, il processo esterno si blocca all'avvio.Perché il processo si blocca se il genitore non consuma stdout/stderr in Java?

La mia domanda è perché funziona in questo modo. La mia ipotesi è che JVM reindirizzi stdout/stderr del processo eseguito alle pipe e se le pipe non hanno spazio le scritture al blocco pipe. Ha senso?

Ora mi chiedo perché Java in questo modo. Qual è la logica alla base di questo design?

+3

Che altro si dovrebbe fare? Gli stream dovrebbero essere semplicemente ignorati? In questo modo il chiamante è costretto a leggere i flussi. –

+0

Come ha detto @UwePlonus; se vuoi davvero ignorarli tutti, crea solo una sorta di 'DevNull {Input, Output} Stream' o qualcosa (la JVM potrebbe anche averlo già, non lo so) – fge

+1

BTW, la stessa identica cosa succederà a la riga di comando Unix se si reindirizza std {out, err} su una named pipe e non si legge da esso. – fge

risposta

10

Java non fa nulla in quest'area. Usa solo i servizi OS per creare le pipe.

Tutti i SO e Windows Unix si comportano allo stesso modo a questo proposito: viene creata una pipe con un 4K tra genitore e figlio. Quando il tubo è pieno (perché un lato non sta leggendo), il processo di scrittura si blocca.

Questo è lo standard fin dall'inizio dei tubi. Non c'è molto che Java possa fare.

Ciò che si può sostenere è che l'API di processo in Java è maldestra e non ha buone impostazioni predefinite come semplicemente la connessione di flussi figlio allo stesso stdin/stdout come genitore, a meno che lo sviluppatore non li sostituisca con qualcosa di specifico.

Penso che ci siano due ragioni per l'API corrente. Prima di tutto, gli sviluppatori Java (ovvero i ragazzi di Sun/Oracle) sanno esattamente come funziona l'API del processo e cosa è necessario fare. Loro sanno così tanto che non è venuto in mente loro che l'API potrebbe essere fonte di confusione.

Il secondo motivo è che non esiste un valore predefinito valido che funzioni per la maggioranza. Non si può realmente connettere lo stdin del genitore e del bambino; se si digita qualcosa sulla console, a quale processo deve andare l'input?

Analogamente, se si collega lo stdout, l'uscita andrà da qualche parte. Se si dispone di un'app Web, potrebbe non esserci alcuna console o l'output potrebbe andare da qualche parte dove nessuno se lo aspetta.

Non si può nemmeno lanciare un'eccezione quando il tubo è pieno poiché ciò può accadere anche durante il normale funzionamento.

+1

Signore, abbiamo bisogno di due thread esterni per leggere sia inputStream che errorStream di ProcessBuilder ed eseguirlo prima di chiamare waitFor()? – saurabheights

6

Si è spiegato nel javadoc of Process:

Per impostazione predefinita, il sottoprocesso creato non dispone di un proprio terminale o console. Tutte le operazioni di I/O standard (ovvero stdin, stdout, stderr) verranno reindirizzate al processo principale, a cui è possibile accedere tramite gli stream ottenuti utilizzando i metodi getOutputStream(), getInputStream() e getErrorStream(). Il processo padre utilizza questi flussi per alimentare l'input e ottenere l'output dal sottoprocesso. Poiché alcune piattaforme native forniscono solo dimensioni del buffer limitate per flussi di input e output standard, l'errore di scrivere prontamente il flusso di input o leggere il flusso di output del sottoprocesso può causare il blocco del sottoprocesso o persino il deadlock.