2015-09-03 57 views
10

Recentemente ho avuto qualche comportamento un po 'strano durante l'esecuzione di comandi su ssh. Sarei interessato a conoscere eventuali spiegazioni per il comportamento riportato di seguito.Perché l'esecuzione di un'attività in background su ssh non riesce se viene allocata una pseudo-tty?

Esecuzione ssh localhost 'touch foobar &' crea un file chiamato foobar come previsto:

[[email protected] ~]$ ssh localhost 'touch foobar &' 
[[email protected] ~]$ ls foobar 
foobar 

Tuttavia eseguendo lo stesso comando, ma con l'opzione -t per forzare l'assegnazione pseudo-tty non riesce a creare foobar:

[[email protected] ~]$ ssh -t localhost 'touch foobar &' 
Connection to localhost closed. 
[[email protected] ~]$ echo $? 
0 
[[email protected] ~]$ ls foobar 
ls: cannot access foobar: No such file or directory 

mio la teoria corrente è che poiché il processo touch è in background, la pseudo-tty viene allocata e non allocata prima che il processo abbia la possibilità di essere eseguito. Certamente l'aggiunta di uno secondo il sonno permette touch per funzionare come previsto:

[[email protected] ~]$ ssh -t localhost 'touch foobar & sleep 1' 
Connection to localhost closed. 
[[email protected] ~]$ ls foobar 
foobar 

Se qualcuno ha una spiegazione definitiva sarei molto interessato a sentirlo. Grazie.

+0

Penso che sia esattamente. Il processo in background è collegato al tty e il tty dying lo uccide. Prova 'nohup touch foobar &' e vedi se funziona e/o 'tocca foobar/dev/null 2> & 1'. –

+0

Entrambi 'ssh -t localhost 'touch foobar < /dev/null >/dev/null 2> & 1 &'' e 'ssh -t localhost 'nohup touch foobar &'' producono lo stesso comportamento. – user414310

+0

Non riescono a creare il file? Ottengo il file creato qui anche per la versione originale per il record. –

risposta

22

Oh, questo è buono.

Questo è correlato al modo in cui i gruppi di processi funzionano, come si comporta bash quando invocato come shell non interattiva con -c e l'effetto di & nei comandi di input.

La risposta presuppone che tu abbia familiarità con il funzionamento del controllo dei lavori in UNIX; se non lo sei, ecco una vista di alto livello: ogni processo appartiene a un gruppo di processi (i processi nello stesso gruppo vengono spesso inseriti come parte di una pipeline di comandi, ad esempio cat file | sort | grep 'word' posizionano i processi in esecuzione cat(1), sort(1) e grep(1) in lo stesso gruppo di processi). bash è un processo come un altro e appartiene anche a un gruppo di processi. I gruppi di processi fanno parte di una sessione (una sessione è composta da uno o più gruppi di processi). In una sessione, esiste al massimo un gruppo di processi, denominato gruppo di processi in primo piano e probabilmente molti gruppi di processi in background. Il gruppo di processi in primo piano ha il controllo del terminale (se vi è un terminale di controllo collegato alla sessione); il leader della sessione (bash) sposta i processi dallo sfondo al primo piano e dal primo piano allo sfondo con tcsetpgrp(3). Un segnale inviato a un gruppo di processi viene consegnato a ogni processo in quel gruppo.

Se il concetto di gruppi di processi e controllo del lavoro è completamente nuovo per te, penso che dovrai leggerlo per comprendere appieno questa risposta. Una grande risorsa per imparare questo è il capitolo 9 di Programmazione avanzata nell'ambiente UNIX (3a edizione).

Detto questo, vediamo cosa sta succedendo qui. Dobbiamo mettere insieme tutti i pezzi del puzzle.

In entrambi i casi, il lato remoto ssh richiama bash(1) con -c. L'indicatore -c causa l'esecuzione di bash(1) come shell non interattiva. Dalla manpage:

Una shell interattiva è uno iniziato senza argomenti non opzione e senza l'opzione -c cui standard input e sono entrambi collegata ai terminali di errore (come determinato da isatty (3)), oppure uno ha iniziato con l'opzione -i.PS1 è impostato e $ - include i se bash è interattivo, consentendo uno script di shell o un file di avvio per testare questo stato .

Inoltre, è importante sapere che controllo dei job è disabilitata quando bash viene avviato in modalità non interattiva. Ciò significa che bash non creerà un gruppo di processi separato per eseguire il comando, poiché il controllo dei lavori è disabilitato, non sarà necessario spostare questo comando tra foreground e background, quindi potrebbe anche rimanere nello stesso gruppo di processi di bash . Ciò avverrà indipendentemente dal fatto che sia stata forzata l'allocazione del PTY su ssh con -t.

Tuttavia, l'uso di & ha l'effetto collaterale di causare l'attesa della terminazione del comando da parte della shell (anche se il controllo dei lavori è disabilitato). Dalla manpage:

Se un comando è terminato dall'operatore controllo &, il guscio esegue il comando in background in una subshell. La shell non non attende il completamento del comando e lo stato di ritorno è 0. Comandi separati da a; sono eseguiti sequenzialmente; la shell attende per ogni comando da terminare a turno. Lo stato di ritorno è lo stato di uscita dell'ultimo comando eseguito.

Così, in entrambi i casi, bash non aspettare per l'esecuzione dei comandi, e touch(1) saranno eseguiti nello stesso gruppo di processi come bash(1).

Ora, considera cosa succede quando un dirigente di sessione esce. Citando dalla pagina di manuale setpgid(2):

Se una sessione ha un terminale di controllo, e la bandiera CLOCAL per quella terminale non è impostato, e si verifica un hangup terminale, quindi il leader di sessione viene inviato un SIGHUP. Se il responsabile della sessione termina, verrà inviato anche un segnale SIGHUP a ciascun processo nel gruppo di fattori in primo piano del terminale di controllo.

(enfasi mia)

Quando non si utilizza -t

Quando non si utilizza -t, non v'è alcuna allocazione PTY dal lato a distanza, in modo da bash non è un responsabile della sessione, e in effetti nessuna nuova sessione viene creata. Poiché sshd è in esecuzione come un demone, il processo bash che è biforcuto + exec() 'd non avrà un terminale di controllo. Di conseguenza, anche se la shell termina molto rapidamente (probabilmente prima di touch(1)), non vi è alcun SIGHUP inviato al gruppo di processi, perché bash non era un leader di sessione (e non esiste un terminale di controllo). Quindi tutto funziona.

Quando si utilizza -t

-t forze assegnazione PTY, il che significa che il lato remoto ssh chiamerà setsid(2), allocare un + forcella un nuovo processo pseudo-terminale con forkpty(3), collegare l'ingresso PTY dispositivo master e emettere gli endpoint del socket che portano al computer e infine eseguire bash(1). forkpty(3) apre il lato slave PTY nel processo biforcato che diventerà bash; poiché non esiste un terminale di controllo per la sessione corrente e un dispositivo terminale viene aperto, il dispositivo PTY diventa il terminale di controllo per la sessione e bash diventa il leader della sessione.

Quindi la stessa cosa accade di nuovo: touch(1) viene eseguito nello stesso gruppo di processi, ecc., Yadda yadda. Il punto è, questa volta, lì è un leader di sessione e un terminale di controllo. Quindi, dal momento che bash non si preoccupa di aspettare a causa dello &, quando esce, SIGHUP viene consegnato al gruppo di processi e touch(1) muore prematuramente.

Chi nohup

nohup(1) non funziona qui perché non v'è ancora una condizione di competizione. Se bash(1) termina prima nohup(1) ha la possibilità di impostare la gestione del segnale necessaria e il reindirizzamento di file, esso non avrà alcun effetto (che è probabilmente ciò che accade)

Una possibile soluzione

con forza riattivare il controllo dei job lo risolve In bash, lo fai con set -m. Questo funziona:

ssh -t localhost 'set -m ; touch foobar &' 

o forza bash di attesa per touch(1) completare:

ssh -t localhost 'touch foobar & wait `pgrep touch`' 
+0

Grazie per aver dato una spiegazione così chiara e dettagliata, molto apprezzata. – user414310

+1

@ user414310 Contento di aver potuto aiutare. Devo dire, ho adorato la domanda. Grazie per una grande domanda :) –

Problemi correlati