2012-03-22 11 views
5

Per ragioni che non entrerò, devo eseguire una variante di 'top -m io -d 2 10' all'interno di un sottoprocesso da un thread Python su FreeBSD 8.1. Il problema è che, a volte, SIGTTOU viene prodotto (in determinate condizioni dipendenti dal codice che non ho ancora decifrato), interrompendone la parte superiore e il thread completamente. Altre volte, sembra che SIGTTOU non sia stato prodotto, ma la parte superiore o il thread si bloccano comunque.L'esecuzione di "top" nella filettatura produce SIGTTOU

L'output dall'alto dovrebbe generare due serie di statistiche IO per i primi 10 processi del sistema, in cui il primo set è numeri "assoluti" e il secondo set è la differenza incrementale delle statistiche dall'ultimo set, uno secondo prima. L'esecuzione di questo comando sul terminale o all'interno di uno script di shell, indipendentemente dal reindirizzamento dell'output, funziona correttamente.

Quando si verifica il problema, sembra che "top" scriva il primo set di output, ma poi blocca/riceve SIGTTOU prima che possa emettere il secondo set. Nel seguente codice di esempio, solo un set di statistiche di processo viene scritto nel file di output.

Ho scoperto il segnale SIGTTOU che esegue lo script python sotto "truss", ma sembra che le interazioni tra "truss" e "top" possano essere un argomento confondente, poiché semplicemente eseguire truss top -d 2 produce il segnale e si blocca, come di seguito :

... 
ioctl(1,TIOCGETA,0xffffe460)    = 0 (0x0) 
ioctl(1,TIOCGETA,0xc6b138)   = 0 (0x0) 
ioctl(1,TIOCGETA,0xffffe410)    = 0 (0x0) 
ioctl(1,TIOCGWINSZ,0xffffe460)   = 0 (0x0) 
ioctl(1,TIOCGWINSZ,0xffffe930)   = 0 (0x0) 
ioctl(1,TIOCGETA,0x50e560)   = 0 (0x0) 
sigprocmask(SIG_BLOCK,SIGINT|SIGQUIT|SIGTSTP,0x0) = 0 (0x0) 
ioctl(1,TIOCGETA,0x50e560)   = 0 (0x0) 
SIGNAL 22 (SIGTTOU) 

Ecco uno script Python di esempio che riproduce la caduta e/o SIGTTOU:

import subprocess 
from threading import Thread 

def run(): 
    with open("top.log", "wb") as f: 
     subprocess.Popen(("/usr/bin/top", "-m", "io", "-d", "2", "10"), stdout=f, stderr=f, stdin=subprocess.PIPE).communicate() 

if __name__ == "__main__": 
    th = Thread(target=run) 
    print "Starting" 
    th.start() 
    th.join() 

Durante la mia ultima corsa attraverso, questo programma di esempio non ha prodotto SIGTTOU, ma superiore fatto appendere. spettacoli Truss:

.... 
open("/usr/local/lib/python2.7/lib-tk/_heapq.pyc",O_RDONLY,0666) ERR#2 'No such file or directory' 
stat("/usr/local/lib/python2.7/lib-dynload/_heapq",0x7fffffffa500) ERR#2 'No such file or directory' 
open("/usr/local/lib/python2.7/lib-dynload/_heapq.so",O_RDONLY,0666) = 5 (0x5) 
fstat(5,{ mode=-rwxr-xr-x ,inode=238187,size=22293,blksize=16384 }) = 0 (0x0) 
sigprocmask(SIG_BLOCK,SIGHUP|SIGINT|SIGQUIT|SIGKILL|SIGPIPE|SIGALRM|SIGTERM|SIGURG|SIGSTOP|SIGTSTP|SIGCONT|SIGCHLD|SIGTTIN|SIGTTOU|SIGIO|SIGXCPU|SIGXFSZ|SIGVTALRM|SIGPROF|SIGWINCH|SIGINFO|SIGUSR1|SIGUSR2,0x0) = 0 (0x0) 
open("/usr/local/lib/python2.7/lib-dynload/_heapq.so",O_RDONLY,057) = 6 (0x6) 
fstat(6,{ mode=-rwxr-xr-x ,inode=238187,size=22293,blksize=16384 }) = 0 (0x0) 
pread(0x6,0x80074c2e0,0x1000,0x0,0xffff800800653120,0x8080808080808080) = 4096 (0x1000) 
mmap(0x0,1069056,PROT_NONE,MAP_PRIVATE|MAP_ANON|MAP_NOCORE,-1,0x0) = 34389442560 (0x801c54000) 
mmap(0x801c54000,12288,PROT_READ|PROT_EXEC,MAP_PRIVATE|MAP_FIXED|MAP_NOCORE,6,0x0) = 34389442560 (0x801c54000) 
mmap(0x801d56000,12288,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_FIXED,6,0x2000) = 34390499328 (0x801d56000) 
mmap(0x0,36864,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANON,-1,0x0) = 34366377984 (0x800655000) 
close(6)      = 0 (0x0) 
mmap(0x0,832,PROT_READ|PROT_WRITE,MAP_ANON,-1,0x0) = 34366414848 (0x80065e000) 
munmap(0x80065e000,832)    = 0 (0x0) 
sigprocmask(SIG_SETMASK,0x0,0x0)   = 0 (0x0) 
sigprocmask(SIG_BLOCK,SIGHUP|SIGINT|SIGQUIT|SIGKILL|SIGPIPE|SIGALRM|SIGTERM|SIGURG|SIGSTOP|SIGTSTP|SIGCONT|SIGCHLD|SIGTTIN|SIGTTOU|SIGIO|SIGXCPU|SIGXFSZ|SIGVTALRM|SIGPROF|SIGWINCH|SIGINFO|SIGUSR1|SIGUSR2,0x0) = 0 (0x0) 
sigprocmask(SIG_SETMASK,0x0,0x0)   = 0 (0x0) 
close(5)      = 0 (0x0) 
close(4)      = 0 (0x0) 
close(3)      = 0 (0x0) 
close(2)      = 0 (0x0) 
fstat(1,{ mode=crw------- ,inode=102,size=0,blksize=4096 }) = 0 (0x0) 
ioctl(1,TIOCGETA,0xffffe400)    = 0 (0x0) 
Starting 
write(1,"Starting\n",9)    = 9 (0x9) 
sigprocmask(SIG_BLOCK,SIGHUP|SIGINT|SIGQUIT|SIGILL|SIGTRAP|SIGABRT|SIGEMT|SIGFPE|SIGKILL|SIGBUS|SIGSEGV|SIGSYS|SIGPIPE|SIGALRM|SIGTERM|SIGURG|SIGSTOP|SIGTSTP|SIGCONT|SIGCHLD|SIGTTIN|SIGTTOU|SIGIO|SIGXCPU|SIGXFSZ|SIGVTALRM|SIGPROF|SIGWINCH|SIGINFO|SIGUSR1|SIGUSR2,0x0) = 0 (0x0) 
_umtx_op(0x7fffffffe1d8,0x3,0x1,0x0,0x0,0x0)  = 0 (0x0) 
sigprocmask(SIG_BLOCK,SIGHUP|SIGINT|SIGQUIT|SIGABRT|SIGEMT|SIGKILL|SIGSYS|SIGPIPE|SIGALRM|SIGTERM|SIGURG|SIGSTOP|SIGTSTP|SIGCONT|SIGCHLD|SIGTTIN|SIGTTOU|SIGIO|SIGXCPU|SIGXFSZ|SIGVTALRM|SIGPROF|SIGWINCH|SIGINFO|SIGUSR1|SIGUSR2,SIGHUP|SIGINT|SIGQUIT|SIGILL|SIGTRAP|SIGABRT|SIGEMT|SIGFPE|SIGBUS|SIGSEGV|SIGSYS|SIGPIPE|SIGALRM|SIGTERM|SIGURG|SIGTSTP|SIGCONT|SIGCHLD|SIGTTIN|SIGTTOU|SIGIO|SIGXCPU|SIGXFSZ|SIGVTALRM|SIGPROF|SIGWINCH|SIGINFO|SIGUSR1|SIGUSR2) = 0 (0x0) 
sigprocmask(SIG_SETMASK,SIGHUP|SIGINT|SIGQUIT|SIGILL|SIGTRAP|SIGABRT|SIGEMT|SIGFPE|SIGBUS|SIGSEGV|SIGSYS|SIGPIPE|SIGALRM|SIGTERM|SIGURG|SIGTSTP|SIGCONT|SIGCHLD|SIGTTIN|SIGTTOU|SIGIO|SIGXCPU|SIGXFSZ|SIGVTALRM|SIGPROF|SIGWINCH|SIGINFO|SIGUSR1|SIGUSR2,0x0) = 0 (0x0) 
sigprocmask(SIG_BLOCK,SIGHUP|SIGINT|SIGQUIT|SIGABRT|SIGEMT|SIGKILL|SIGSYS|SIGPIPE|SIGALRM|SIGTERM|SIGURG|SIGSTOP|SIGTSTP|SIGCONT|SIGCHLD|SIGTTIN|SIGTTOU|SIGIO|SIGXCPU|SIGXFSZ|SIGVTALRM|SIGPROF|SIGWINCH|SIGINFO|SIGUSR1|SIGUSR2,SIGHUP|SIGINT|SIGQUIT|SIGILL|SIGTRAP|SIGABRT|SIGEMT|SIGFPE|SIGBUS|SIGSEGV|SIGSYS|SIGPIPE|SIGALRM|SIGTERM|SIGURG|SIGTSTP|SIGCONT|SIGCHLD|SIGTTIN|SIGTTOU|SIGIO|SIGXCPU|SIGXFSZ|SIGVTALRM|SIGPROF|SIGWINCH|SIGINFO|SIGUSR1|SIGUSR2) = 0 (0x0) 
sigprocmask(SIG_SETMASK,SIGHUP|SIGINT|SIGQUIT|SIGILL|SIGTRAP|SIGABRT|SIGEMT|SIGFPE|SIGBUS|SIGSEGV|SIGSYS|SIGPIPE|SIGALRM|SIGTERM|SIGURG|SIGTSTP|SIGCONT|SIGCHLD|SIGTTIN|SIGTTOU|SIGIO|SIGXCPU|SIGXFSZ|SIGVTALRM|SIGPROF|SIGWINCH|SIGINFO|SIGUSR1|SIGUSR2,0x0) = 0 (0x0) 
mmap(0x7fffffbde000,135168,PROT_READ|PROT_WRITE,MAP_STACK,-1,0x0) = 140737484021760 (0x7fffffbde000) 
mprotect(0x7fffffbde000,4096,PROT_NONE)  = 0 (0x0) 
thr_new(0x7fffffffe220,0x68,0x800a9f4c0,0x186fc,0xffffffff,0x0) = 0 (0x0) 
sigprocmask(SIG_SETMASK,0x0,0x0)   = 0 (0x0) 
mmap(0x0,2097152,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANON,-1,0x0) = 34390511616 (0x801d59000) 
mmap(0x801f59000,684032,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANON,-1,0x0) = 34392608768 (0x801f59000) 
munmap(0x801d59000,684032)   = 0 (0x0) 
_umtx_op(0x8010127f8,0x10,0x1,0x0,0x0,0x0) = 0 (0x0) 
_umtx_op(0x800e0b438,0xf,0x0,0x0,0x0,0x0) = 0 (0x0) 
_umtx_op(0x800e0b438,0x10,0x1,0x0,0x0,0x0) = 0 (0x0) 
_umtx_op(0x800e0b438,0x10,0x1,0x0,0x0,0x0) = 0 (0x0) 
_umtx_op(0x800e0b438,0x10,0x1,0x0,0x0,0x8080808080808080) = 0 (0x0) 
open("top.log",O_WRONLY|O_CREAT|O_TRUNC,0666) = 2 (0x2) 
fstat(2,{ mode=-rw-r--r-- ,inode=70860,size=0,blksize=16384 }) = 0 (0x0) 
pipe(0x7fffffbfd910)     = 0 (0x0) 
pipe(0x7fffffbfd870)     = 0 (0x0) 
fcntl(6,F_GETFD,)    = 0 (0x0) 
fcntl(6,F_SETFD,FD_CLOEXEC)   = 0 (0x0) 
sigprocmask(SIG_BLOCK,SIGHUP|SIGINT|SIGQUIT|SIGABRT|SIGEMT|SIGKILL|SIGSYS|SIGPIPE|SIGALRM|SIGTERM|SIGURG|SIGSTOP|SIGTSTP|SIGCONT|SIGCHLD|SIGTTIN|SIGTTOU|SIGIO|SIGXCPU|SIGXFSZ|SIGVTALRM|SIGPROF|SIGWINCH|SIGINFO|SIGUSR1|SIGUSR2,SIGHUP|SIGINT|SIGQUIT|SIGILL|SIGTRAP|SIGABRT|SIGEMT|SIGFPE|SIGBUS|SIGSEGV|SIGSYS|SIGPIPE|SIGALRM|SIGTERM|SIGURG|SIGTSTP|SIGCONT|SIGCHLD|SIGTTIN|SIGTTOU|SIGIO|SIGXCPU|SIGXFSZ|SIGVTALRM|SIGPROF|SIGWINCH|SIGINFO|SIGUSR1|SIGUSR2) = 0 (0x0) 
fork()      = 21503 (0x53ff) 
sigprocmask(SIG_SETMASK,SIGHUP|SIGINT|SIGQUIT|SIGILL|SIGTRAP|SIGABRT|SIGEMT|SIGFPE|SIGBUS|SIGSEGV|SIGSYS|SIGPIPE|SIGALRM|SIGTERM|SIGURG|SIGTSTP|SIGCONT|SIGCHLD|SIGTTIN|SIGTTOU|SIGIO|SIGXCPU|SIGXFSZ|SIGVTALRM|SIGPROF|SIGWINCH|SIGINFO|SIGUSR1|SIGUSR2,0x0) = 0 (0x0) 
close(6)      = 0 (0x0) 
close(3)      = 0 (0x0) 
read(5,0x801e31024,1048576)   = 0 (0x0) 
close(5)      = 0 (0x0) 
fcntl(4,F_GETFL,)    = 2 (0x2) 
fstat(4,{ mode=p--------- ,inode=0,size=0,blksize=4096 }) = 0 (0x0) 
close(4)      = 0 (0x0) 

Ho guardato in SIGTTOU ed hanno trovato riferimenti alla bandiera tostop termios, e ho truccati con esso nel thread principale, nel thread figlio, e nell'ambiente invocando Python, tutti inutilmente. È stato un processo educativo, ma non ci sono ancora.

Ho eseguito test per assicurarmi che il processo principale sia stato creato e resti nel gruppo di processi del processo Python (basato sulla documentazione SIGTTOU, se così non fosse, questa sarebbe la ragione per SIGTTOU), e sembra a posto: il PGRP finisce per essere uguale al Python PID/PGRP.

Ho provato a eseguire "top" con subprocess.check_output e con .Popen() utilizzando shell = True, shell = False e reindirizzando std {out, err, in} dappertutto, nessuno dei quali sembra per cambiare questo risultato finale. Ho provato a eseguire 'top' usando un comando '/ bin/sh -c' eseguito attraverso il sottoprocesso, anche senza alcun risultato.

Senza fare qualcosa di semi-strano come eseguire "top" all'interno di uno script di shell richiamato dal mio thread Python, o ricorrere a os.fork() invece di usare il threading, come posso aggirare questo problema e qual è la radice causa?

risposta

1

SIGTTOU viene usato quando un processo tenta di modificare il terminale di controllo:

Se l'implementazione supporta job-controllo, se non diversamente specificato, i processi in uno sfondo da elaborare gruppo sono limitati nel loro utilizzo del terminale -controllo-funzioni (vedi termios (3C)). I tentativi di eseguire queste funzioni fanno sì che al gruppo di processi venga inviato il segnale SIGTTOU. Se il processo chiamante ignora o blocca il segnale SIGTTOU, il tentativo di eseguire una funzione di controllo procede senza inviare il segnale SIGTTOU.

(da Terminal access control)

Cosa significa? Significa che il top sta cercando di cambiare qualcosa sul terminale e viene detto che non può farlo, e l'azione predefinita per SIGTTOU è quella di interrompere l'esecuzione del processo (il blocco durante la chiamata).

Quello che potresti provare a fare è usare fork() per farlo entrare nel suo gruppo di processi, uno senza un terminale di controllo. Questo dovrebbe consentire a top di chiamare qualsiasi cosa voglia chiamare, e poiché non c'è un terminale di controllo, semplicemente non avrà alcun effetto.

Tuttavia top non è mai stato concepito per essere chiamato in modo non interattivo, non è possibile ottenere le stesse informazioni utilizzando ps?


Questo post del blog: http://www.technovelty.org/tips/sigttou-and-switching-to-canonical-mode.html spiega anche che cosa sta succedendo in modo chiaro. Spero che sia d'aiuto.

+0

che corrisponde a quello che ho scoperto da allora. Ancora oggi non sono ancora sicuro di quale top stia cercando di modificare il terminale (o perché). Sfortunatamente sto lavorando a questo in un sistema legacy, quindi ho effettivamente bisogno di produrre l'output raw di 'top' nel mio codice. Ho finito per aggirare questo problema usando la libreria 'pty' di Python e funzionando al suo interno. Tuttavia, a causa di altre difficoltà tecniche, ciò significava anche dover analizzare e rimuovere in minima parte le sequenze ANSI dall'output. (Ma ora funziona!) :) – Anthem

0

Hai provato a utilizzare l'opzione -b con top? È pensato per lavori batch e terminali stupidi e può dire di non fare, qualunque cosa stia facendo che innesca il segnale ...

2

Mi rendo conto che questa domanda è un po 'vecchia, ma se stai ancora correndo in errori, mi piacerebbe eseguire il debug di questo nella sporcizia.

Causa principale: Il tuo SIGTTOU si sta verificando perché il vostro interprete Python è fork per creare il thread in background quando si chiama th = Thread(target=run) e top non è stato detto/non sa che non dovrebbe usare il terminale. Stai vedendo i segnali perché top si sta facendo frisky e sta provando a scrivere sul terminale (o cambiare la sua modalità di emulazione) come processo di sfondo quando non si consente che questo comportamento si verifichi nelle impostazioni TTY.

man stty spiega questo più succinto di quanto vorrei:

tostop (-tostop) 
      Send (do not send) SIGTTOU for background output. This causes back- 
      ground jobs to stop if they attempt terminal output. 

Soluzione: Consentire thread in background per lanciare l'uscita sul terminale durante la corsa dello script (stty -tostop; python my_script.py; stty tostop) o aggiungere la ('-n') bandiera la chiamata al sottoprocesso di top.


Elaborazione: solo processo per ogni gruppo può essere in primo piano e il resto rimane sullo sfondo - il processo diforeground gestisce I/O da un terminale e il resto deve rimanere come sfondo processi o vedrai i segnali di controllo del lavoro che iniziano a essere lanciati (es. SIGTTIN/SIGTTOU).

Durante l'esecuzione dello script Python, credo che si verifica quanto segue:

$SHELL #(controls TTY) 
$ python my_script.py #(tcsetpgrp() is called to hand off control of TTY) 
~~~ heck yeah, snake party ~~~ 
th = Thread(target=run) #(run target=proc in background) 
print "Starting" #(still okay -- this gets handed up to the foreground interpreter) 
th.start() 
#(here be dragons, std i/o in background fork) 
subprocess.Popen(("/usr/bin/top", "-m", "io", "-d", "2", "10").communicate() 

Ho controllato il FreeBSD manual for its top implementation e ho trovato il seguente pistola fumante:

DESCRIPTION 
     Top displays the top processes on the system and periodically updates 
     this information... 

     Top makes a distinction between terminals that support advanced capa- 
     bilities and those that do not...If the output of top is redi- 
     rected to a file, it acts as if it were being run on a dumb terminal. 

... 
OPTIONS 
    -i  Use "interactive" mode. In this mode, any input is immediately 
     read for processing. See the section on "Interactive Mode" for 
     an explanation of which keys perform what functions. After the 
     command is processed, the screen will immediately be updated, 
     even if the command was not understood. This mode is the 
     default when standard output is an intelligent terminal. 
    ... 
    -n  Use "non-interactive" mode. This is identical to "batch" mode. 

Mentre top non sappi che viene eseguito in un processo in background (la gestione dei file viene eseguita con il tuo gestore contesto Python) e non hai specificato la modalità non interattiva, si presume che sia libero di usare il tty - il che significa che probabilmente S ee SIGTTIN segnala se top riceve segnali STDIN e SIGTTOU quando i comandi vengono elaborati e tenta di aggiornare lo schermo.

Di particolare interesse da cima implementazione di FreeBSD, la differenza di ciò che accade quando viene chiamato in modo interattivo o no:

La tua idea di aggiungere shell=True verifica questa teoria come è sets the child process of 'top' to the PID of the shell that subprocess.Popen(..) spawns, che è ancora in uno sfondo Filo di Python

(scuse N.B.: Io non hanno accesso a una serie di FreeBSD 8.1 per verificare il comportamento del sistema operativo ospite al momento.)