Запуск 'top' в потоке создает SIGTTOU - PullRequest
5 голосов
/ 22 марта 2012

По причинам, в которые я не буду вдаваться, мне нужно запустить вариант 'top -m io -d 2 10' внутри подпроцесса из потока Python во FreeBSD 8.1.Проблема в том, что иногда создается SIGTTOU (при определенных условиях, зависящих от кода, которые я еще не расшифровал), полностью останавливая top и поток.В других случаях кажется, что SIGTTOU не создается, но top или поток все равно застревают.

Выход top должен производить два набора статистики ввода-вывода для 10 лучших процессов в системе, где первый наборэто «абсолютные» числа, а второй набор - это инкрементная разница статистики со времени последнего набора, на одну секунду раньше.Выполнение этой команды в терминале или в сценарии оболочки, независимо от того, перенаправляет ли вывод или нет, работает нормально.

Когда возникает проблема, кажется, что top записывает первый набор выходных данных, но затем зависает /получает SIGTTOU, прежде чем он сможет вывести второй набор.В приведенном ниже примере кода в выходной файл записывается только один набор статистики процесса.

Я обнаружил сигнал SIGTTOU, выполняющий скрипт python в «truss», но кажется, что взаимодействия между «truss» и «Сами top 'могут вызывать затруднения, так как простой запуск truss top -d 2 производит сигнал и зависает, как показано ниже:

...
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)

Вот пример скрипта Python, который воспроизводит зависание и / или 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()

При моем последнем прогоне эта программа-пример не выдает SIGTTOU, но top зависает.Ферма показывает:

....
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)

Я посмотрел на SIGTTOU и нашел ссылки на флаг TOSTOP termios, и я возился с ним в основном потоке, в дочернем потоке и в среде, вызывающей Pythonвсе безрезультатно.Это был образовательный процесс, но я еще не там.

Я провел тесты, чтобы убедиться, что верхний процесс создан и, похоже, остается в группе процессов процесса Python (на основедокументация SIGTTOU, если это не так, это было бы причиной для SIGTTOU), и это выглядит хорошо: PGRP заканчивается тем же, что и PID / PGRP Python.

Я пытался запустить 'top 'с subprocess.check_output и .Popen () с использованием shell = True, shell = False и перенаправлением std {out, err, in} повсюду, ни один из которых, похоже, не меняет этого конечного результата.Я попытался запустить top с помощью команды «/ bin / sh -c», выполняемой через подпроцесс, также безрезультатно.

Без выполнения чего-то наполовину странного, такого как запуск top в скрипте оболочки, которыймой поток Python вызывает или использует os.fork () вместо использования многопоточности, как я могу обойти эту проблему и какова ее основная причина?

Ответы [ 3 ]

2 голосов
/ 22 декабря 2014

Я понимаю, что этот вопрос немного стар, но если вы все еще сталкиваетесь с ошибками, я бы хотел отладить это в грязь.

Основная причина : Ваш SIGTTOU происходит, потому что ваш интерпретатор Python разветвляется, чтобы создать фоновый поток, когда вы вызываете th = Thread(target=run), а top не было сказано / не знает, что это не должно не использовать терминал. Вы видите сигналы, потому что top становится резвым и пытается записать на терминал (или изменить его режим эмуляции) как процесс background , когда вы не допустили такого поведения в настройках TTY.

man stty объясняет это более кратко, чем я:

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

Обходной путь : разрешить фоновым потокам выводить выходные данные на терминал во время выполнения сценария (stty -tostop; python my_script.py; stty tostop) или добавить флаг ('-n') к вашему вызову подпроцесса top.


Разработка: Только один процесс на группу может находиться на переднем плане, а остальные остаются на заднем плане - процесс foreground обрабатывает ввод / вывод из tty, а остальные должны оставайтесь в качестве фоновых процессов, иначе вы начнете получать сигналы управления заданиями (например, SIGTTIN / SIGTTOU).

Во время выполнения вашего скрипта Python, я считаю, происходит следующее:

$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() 

Я проверил руководство FreeBSD для его наилучшей реализации и обнаружил следующее дымящееся ружье:

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.

Принимая во внимание, что top не знает, что он запускается в фоновом процессе (передача файла выполняется с помощью вашего диспетчера контекста Python), и вы не указали неинтерактивный режим, он предполагает, что его можно использовать бесплатно. tty - это означает, что вы, вероятно, увидите сигналы SIGTTIN, если top захватывает любые сигналы STDIN и SIGTTOU при обработке команд и пытается обновить экран.

Особый интерес представляет лучшая реализация FreeBSD, разница в том, что происходит при интерактивном вызове или нет:

Ваша идея добавить shell=True подтверждает эту теорию, поскольку она устанавливает дочерний процесс top в PID оболочки, которая subprocess.Popen(..) порождает , которая все еще находится в фоновом потоке Python.

(n.b. Извинения: у меня нет доступа к хосту FreeBSD 8.1 для проверки поведения на вашей хост-ОС прямо сейчас.)

1 голос
/ 01 ноября 2012

SIGTTOU используется, когда процесс пытается изменить управляющий терминал:

Если реализация поддерживает управление заданиями, если не указано иное, процессы в фоновой группе процессов ограничены в использовании ими функций управления терминалом (см. Termios (3C)). Попытки выполнить эти функции приводят к тому, что группе процессов отправляется сигнал SIGTTOU. Если вызывающий процесс либо игнорирует, либо блокирует сигнал SIGTTOU, попытка выполнить функцию управления продолжается без отправки сигнала SIGTTOU.

Терминал контроля доступа )

Что это значит? Это означает, что top пытается изменить что-то в терминале и получает сообщение о том, что он не может этого сделать, и действие по умолчанию для SIGTTOU - остановить запуск процесса (зависание, как вы его называете).

То, что вы можете попытаться сделать, это использовать fork(), чтобы поместить его в свою собственную группу процессов, без контрольного терминала. Это должно позволить top вызывать то, что он хочет вызвать, и, поскольку нет управляющего терминала, он просто не будет иметь никакого эффекта.

Несмотря на то, что top никогда не вызывался неинтерактивно, вы не можете получить ту же информацию, используя ps?


Этот пост в блоге: http://www.technovelty.org/tips/sigttou-and-switching-to-canonical-mode.html также ясно объясняет, что происходит. Надеюсь, это поможет.

0 голосов
/ 11 апреля 2013

Вы пытались использовать опцию -b с верхом? Он предназначен для пакетных заданий и тупых терминалов и может указывать top не делать, что бы он ни делал, что вызывает сигнал ...

...