Java – Esecuzione comandi su CLI parte II
Se vi ricordate di un pezzo di codice scritto da me e che funzionava particolarmente bene per invocare comandi su UNIX… Beh… Scordatevelo! O meglio, dovrete fare qualche piccola modifica.
Il codice in questione sembra, infatti, causare un piccolo problema chiamato deadlock.
La cosa non si presenta sempre, ma dipende fortemente dalla quantità di output che genera il comando e da come il sistema operativo gestisce il buffering della system call “write”. Lanciando truss da dentro al codice java, infatti, notavo che l’esecuzione delle system call si bloccava in attesa:
fstat64(1, 0xFFBEDFA8) = 0
brk(0x00068A70) = 0
brk(0x0006AA70) = 0
write(1, " D A T A ".., 5120) = 5120
write(1, " * - > *".., 5120) = 5120
llseek(0, 0, SEEK_CUR) Err#29 ESPIPE
write(1, " * - > * * * * * ".., 635) (sleeping...)
Non è stato facilissimo arrivare alla causa, il metodo è infatti molto semplice, ma adesso che ho identificato il problema l’errore appare quasi evidente. Il problema risiede infatti nel canale di standard error letto prima dello standard output:
- Prima di poter leggere lo standard error è necessario che il processo che esegue il comando termini, e non è detto che vi siano errori;
- Prima di poter avere tutto l’output disponibile c’è bisogno di qualcuno che consumi i dati nel buffer, ma quel qualcuno è in attesa sullo standard error;
- Deadlock!
Sinceramente, questa spiegazione, seppur sensata non mi convince ancora appieno e appena avrò più tempo analizzerò meglio i trace delle system call, nonostante ciò riporto di seguito la versione del metodo che risolve il problema.
String result = null;
String temp = null;
BufferedReader stdout = null;
BufferedReader stderr = null;
Process proc = Runtime.getRuntime().exec(cmd);
/*consume the output*/
stdout = new BufferedReader(new InputStreamReader(
proc.getInputStream()));
temp = stdout.readLine();
while(temp!=null){
if(result==null)
result = temp+"\n";
else
result += temp+"\n";
temp = stdout.readLine();
}
stdout.close();
/*check if errors occurred*/
stderr = new BufferedReader(new InputStreamReader(
proc.getErrorStream()));
temp = stderr.readLine();
if(temp!=null){
result = temp+"\n";
temp = stderr.readLine();
while(temp!=null){
result += temp+"\n";
temp = stderr.readLine();
}
stderr.close();
throw new IOException(result);
}
stderr.close();
/*if no exceptions occurred then proceed to return the output*/
return result;
}
Questo è quanto, il metodo main d’esempio del post precedente però è ancora valido :)





No comments yet.