gpt4 book ai didi

python - 在线程中运行 'top' 会产生 SIGTTOU

转载 作者:太空狗 更新时间:2023-10-30 01:23:43 26 4
gpt4 key购买 nike

出于我不会深入探讨的原因,我需要在 FreeBSD 8.1 上的 Python 线程的子进程中运行“top -m io -d 2 10”的变体。问题是,似乎有时会生成 SIGTTOU(在我尚未破译的某些代码相关条件下),从而完全停止 top 和线程。其他时候,似乎没有产生 SIGTTOU,但 top 或线程仍然卡住了。

top 的输出应该为系统上的前 10 个进程生成两组 IO 统计信息,其中第一组是“绝对”数字,第二组是统计信息自上一组以来的增量差异,一秒更早。在终端或 shell 脚本中运行此命令,无论是否重定向输出,都可以正常工作。

出现问题时,“top”似乎写入了第一组输出,但在它可以输出第二组之前挂起/接收 SIGTTOU。在下面的示例代码中,只有一组进程统计信息被写入输出文件。

我发现 SIGTTOU 信号在“truss”下运行 python 脚本,但似乎“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)

这是一个重现挂起和/或 SIGTTOU 的示例 Python 脚本:

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 最终与 Python PID/PGRP 相同。

我已经尝试使用 subprocess.check_output 和 .Popen() 使用 shell=True、shell=False 运行“top”,并在所有地方重定向 std{out,err,in},似乎没有一个改变这个最终结果。我尝试使用通过子进程执行的“/bin/sh -c”命令运行“top”,但也无济于事。

如果不做一些半奇怪的事情,比如在我的 Python 线程调用的 shell 脚本中运行“top”,或者求助于 os.fork() 而不是使用线程,我该如何解决这个问题,根本原因是什么?

最佳答案

我意识到这个问题有点老了,但如果你仍然遇到错误,我很乐意调试它。

根本原因:您的 SIGTTOU 发生是因为您的 Python 解释器在您调用 th = Thread(target=run) 时 fork 创建后台线程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 的子进程调用。


详细说明:只有一个每个组的进程可以在前台,其余的留在后台——前台进程处理来自 tty 的 I/O,而其余部分必须保留为后台进程,否则您会看到作业控制信号开始被抛出(例如 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 manual for its top implementation我发现了以下确凿的证据:

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——这意味着如果 top 在处理命令并尝试更新屏幕时获取任何 STDIN 和 SIGTTOU 信号,您可能会看到 SIGTTIN 信号。

对 FreeBSD 的顶级实现特别感兴趣的是,交互调用与非交互调用时发生的差异:

您添加 shell=True 的想法验证了这一理论 sets the child process of 'top' to the PID of the shell that subprocess.Popen(..) spawns ,它仍然在后台 Python 线程中。

(n.b. 抱歉:我现在无法访问 FreeBSD 8.1 主机来验证您的主机操作系统上的行为。)

关于python - 在线程中运行 'top' 会产生 SIGTTOU,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9827820/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com