2012-12-05 3 views
25

La domanda breve è: cosa dovrebbe fare una shell se si trova in un gruppo di processi orfani che non possiede il tty? Ma consiglio di leggere la lunga domanda perché è divertente.Cosa devono fare le shell interattive nei gruppi di processi orfani?

Ecco un divertente ed emozionante modo per trasformare il vostro portatile in una stufetta portatile, utilizzando la shell preferita (a meno che non sei uno di quelli tcsh strambi):

#include <unistd.h> 
int main(void) { 
    if (fork() == 0) { 
     execl("/bin/bash", "/bin/bash", NULL); 
    } 
    return 0; 
} 

Questo fa sì che bash di agganciare il CPU al 100%. zsh e fish fanno lo stesso, mentre ksh e tcsh borbottano qualcosa sul controllo dei lavori e poi si chinano, il che è un po 'meglio, ma non molto. Oh, ed è un criminale agnostico: OS X e Linux sono entrambi coinvolti.

La mia spiegazione (potenzialmente errata) è la seguente: la shell figlio rileva che non è in primo piano: tcgetpgrp(0) != getpgrp(). Quindi cerca di fermarsi: killpg(getpgrp(), SIGTTIN). Ma il suo gruppo di processi è orfano, perché il suo genitore (il programma C) era il leader e morì, e SIGTTIN inviato a un gruppo di processi orfani è appena caduto (altrimenti niente potrebbe riavviarlo). Pertanto, la shell figlio non viene arrestata, ma è ancora in background, quindi esegue di nuovo tutto, subito. Risciacqua e ripeti.

La mia domanda è, come può una shell della riga di comando rilevare questo scenario e qual è la cosa giusta da fare? Il mio pensiero è che la shell provi a read da stdin, e appena esce se letto fornisce EIO.

Grazie per i vostri pensieri!

Modifica: Ho provato a fare una lettura di lunghezza zero() su/dev/tty, e che ha avuto successo, che è male. Per ottenere l'EIO, in realtà devo essere pronto a leggere alcuni dati su/dev/tty.

Modifica: Un altro pensiero che ho avuto è stato kill(getpgrp(), 0). Se il gruppo di processi è orfano, allora credo che fallirà sempre. Tuttavia, potrebbe anche fallire perché non ho il permesso di segnalare il responsabile della sessione.

Modifica: Per chi lo trova più tardi, quello che ho finito è descritto allo https://github.com/fish-shell/fish-shell/issues/422. Inoltre, come va il futuro?

+5

+1 per l'osservazione del riscaldatore di spazio portatile. Ho ridacchiato;) –

+2

sarà la migliore suite su http://unix.stackexchange.com/ – mtk

+1

@Will. Come questa non è una domanda di programmazione? – Gilles

risposta

3

Ecco cosa strace dice che sta accadendo:

 
--- SIGTTIN (Stopped (tty input)) @ 0 (0) --- 
rt_sigaction(SIGTTIN, {SIG_IGN, [], SA_RESTORER, 0x7fd5f6989d80}, {SIG_DFL, [], SA_RESTORER, 0x7fd5f6989d80}, 8) = 0 
ioctl(255, TIOCGPGRP, [9954])   = 0 
rt_sigaction(SIGTTIN, {SIG_DFL, [], SA_RESTORER, 0x7fd5f6989d80}, {SIG_IGN, [], SA_RESTORER, 0x7fd5f6989d80}, 8) = 0 
kill(0, SIGTTIN)      = 0 
--- SIGTTIN (Stopped (tty input)) @ 0 (0) --- 
rt_sigaction(SIGTTIN, {SIG_IGN, [], SA_RESTORER, 0x7fd5f6989d80}, {SIG_DFL, [], SA_RESTORER, 0x7fd5f6989d80}, 8) = 0 
ioctl(255, TIOCGPGRP, [9954])   = 0 
rt_sigaction(SIGTTIN, {SIG_DFL, [], SA_RESTORER, 0x7fd5f6989d80}, {SIG_IGN, [], SA_RESTORER, 0x7fd5f6989d80}, 8) = 0 
kill(0, SIGTTIN)      = 0 
[repeat...] 

e qui è il motivo per cui, da jobs.c, bash 4.2:

while ((terminal_pgrp = tcgetpgrp (shell_tty)) != -1) 
    { 
     if (shell_pgrp != terminal_pgrp) 
     { 
      SigHandler *ottin; 

      ottin = set_signal_handler(SIGTTIN, SIG_DFL); 
      kill (0, SIGTTIN); 
      set_signal_handler (SIGTTIN, ottin); 
      continue; 
     } 
     break; 
    } 

Per quanto riguarda cosa fare al riguardo ... bene che è al di là la mia abilità. Ma pensavo che questa fosse un'informazione utile e un po 'troppo per un commento.