2012-03-22 13 views
5

Vì lý do tôi sẽ không tham gia, tôi cần chạy biến thể 'top -m io -d 2 10' trong một tiến trình con từ một chuỗi Python trên FreeBSD 8.1. Vấn đề là, có vẻ như đôi khi SIGTTOU được sản xuất (trong một số điều kiện phụ thuộc vào mã mà tôi chưa giải mã), tạm dừng đầu và toàn bộ luồng. Lần khác, có vẻ như SIGTTOU không được sản xuất, nhưng đầu hoặc các chủ đề bị mắc kẹt anyway. Đầu ra từ đầu sẽ tạo ra hai bộ số liệu thống kê IO cho 10 quy trình hàng đầu trên hệ thống, trong đó tập đầu tiên là số "tuyệt đối" và tập thứ hai là sự khác biệt gia tăng của số liệu thống kê kể từ tập cuối, một thứ hai trước đó. Chạy lệnh này trên thiết bị đầu cuối hoặc trong một kịch bản lệnh shell, cho dù chuyển hướng đầu ra hay không, hoạt động tốt.Chạy 'top' trong chủ đề tạo SIGTTOU

Khi sự cố xảy ra, có vẻ như 'đầu' ghi tập đầu ra đầu tiên, nhưng sau đó treo/nhận SIGTTOU trước khi có thể xuất tập thứ hai. Trong mã mẫu bên dưới, chỉ một bộ số liệu thống kê quy trình được ghi vào tệp đầu ra.

Tôi phát hiện ra tín hiệu SIGTTOU đang chạy tập lệnh python dưới 'giàn', nhưng có vẻ tương tác giữa 'giàn' và 'trên cùng' có thể là vấn đề gây nhiễu, vì chỉ cần chạy truss top -d 2 tạo tín hiệu và treo, như bên dưới :

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

Dưới đây là một kịch bản Python mẫu mà tái tạo được hang và/hoặc 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() 

On chạy cuối cùng của tôi thông qua, chương trình mẫu này đã không tạo SIGTTOU, nhưng đầu đã treo. Giàn show:

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

Tôi đã nhìn vào SIGTTOU và tìm thấy tài liệu tham khảo cho tostop termios cờ, và tôi đã fiddled với nó trong các chủ đề chính, trong thread đứa trẻ, và trong môi trường cách gọi Python, tất cả không có kết quả. Đó là một quá trình giáo dục, nhưng tôi chưa có.

Tôi đã chạy kiểm tra để đảm bảo rằng quy trình hàng đầu được tạo và xuất hiện ở lại trong nhóm quá trình của quy trình Python (dựa trên tài liệu SIGTTOU, nếu không, đây sẽ là lý do cho SIGTTOU), và điều đó có vẻ tốt: PGRP kết thúc bằng với Python PID/PGRP.

Tôi đã thử chạy 'top' với subprocess.check_output và với .Popen() sử dụng shell = True, shell = False và chuyển hướng std {out, err, in} trên toàn bộ địa điểm, không có gì trong số đó để thay đổi kết quả cuối cùng này. Tôi đã thử chạy 'top' bằng cách sử dụng lệnh '/ bin/sh -c' được thực hiện thông qua tiến trình con, cũng không có kết quả.

Không làm điều gì đó bán kỳ lạ như chạy 'trên cùng' trong tập lệnh shell mà chuỗi Python của tôi gọi hoặc sử dụng os.fork() thay vì sử dụng luồng, làm cách nào để khắc phục sự cố này và thư mục gốc là gì nguyên nhân?

Trả lời

1

SIGTTOU được sử dụng khi một quá trình cố gắng để thay đổi nhà ga kiểm soát:

Nếu việc thực hiện hỗ trợ việc kiểm soát, trừ khi có ghi chú khác, các quy trình trong một nền quá trình nhóm bị hạn chế trong việc sử dụng các thiết bị đầu cuối -kiểm soát-chức năng (xem termios (3C)). Nỗ lực thực hiện các chức năng này khiến cho nhóm quá trình được gửi tín hiệu SIGTTOU. Nếu quá trình gọi hoặc bỏ qua hoặc chặn tín hiệu SIGTTOU, cố gắng thực hiện một chức năng điều khiển được tiến hành mà không gửi tín hiệu SIGTTOU.

(Từ Terminal access control)

này có nghĩa là gì? Nó có nghĩa là đầu đang cố gắng để thay đổi một cái gì đó về Terminal và nhận được nói rằng nó không thể làm điều đó, và hành động mặc định cho SIGTTOU là để ngăn chặn quá trình chạy (treo như bạn gọi nó).

Những gì bạn có thể cố gắng làm là sử dụng fork() để đưa nó vào nhóm xử lý riêng, một nhóm không có thiết bị đầu cuối điều khiển. Điều này sẽ cho phép top gọi bất cứ điều gì nó muốn gọi, và vì không có thiết bị đầu cuối điều khiển nó sẽ chỉ đơn giản là không có hiệu lực.

Tuy nhiên, đầu trang không bao giờ được gọi là không tương tác, bạn không thể nhận được cùng một thông tin bằng cách sử dụng ps?


bài viết trên blog này: http://www.technovelty.org/tips/sigttou-and-switching-to-canonical-mode.html cũng giải thích những gì đang xảy ra một cách rõ ràng. Hy vọng nó giúp.

+0

Đó phù hợp với những gì tôi đã phát hiện ra kể từ đó. Cho đến ngày nay mặc dù tôi vẫn không chắc chắn những gì đầu đang cố gắng sửa đổi về thiết bị đầu cuối (hoặc tại sao). Thật không may tôi đang làm việc này vào một hệ thống di sản, vì vậy tôi thực sự cần phải sản xuất đầu ra thô của 'top' trong mã của tôi. Tôi đã kết thúc việc này bằng cách sử dụng thư viện 'pty' của Python và chạy trên cùng bên trong đó. Tuy nhiên, do những khó khăn kỹ thuật khác, điều này cũng có nghĩa là phải phân tích cú pháp và phân tích chuỗi ANSI một cách tối thiểu từ đầu ra. (Nhưng nó hoạt động ngay bây giờ!) :) – Anthem

0

Bạn đã thử sử dụng tùy chọn -b có đầu trang không? Nó có nghĩa là cho công việc hàng loạt và thiết bị đầu cuối câm và có thể nói đầu để không làm, bất cứ điều gì nó đang làm mà gây ra tín hiệu ...

2

Tôi nhận thấy rằng câu hỏi này là một chút cũ, nhưng nếu bạn vẫn đang chạy thành lỗi, tôi rất muốn gỡ lỗi này vào bụi bẩn.

Nguyên nhân: SIGTTOU của bạn đang diễn ra vì thông dịch Python của bạn được forking để tạo ra các sợi nền khi bạn gọi th = Thread(target=run)top đã không được cho biết/không biết nó không nên được sử dụng thiết bị đầu cuối. Bạn đang thấy tín hiệu vì top đang trở nên nguy hiểm và cố gắng ghi vào thiết bị đầu cuối (hoặc thay đổi chế độ mô phỏng) thành một quá trình nền khi bạn không cho phép hành vi này xảy ra trong cài đặt TTY của bạn.

man stty giải thích điều này nhiều hơn một cách ngắn gọn hơn là tôi sẽ:

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

Cách giải quyết: Cho phép chủ đề nền để ném ra vào nhà ga trong thời gian chạy của kịch bản của bạn (stty -tostop; python my_script.py; stty tostop) hoặc thêm ('-n') cờ cuộc gọi subprocess của bạn là top.


Lập: Chỉ có một quá trình cho mỗi nhóm có thể ở mặt trước và phần còn lại vẫn ở chế độ nền - các foreground quá trình xử lý I/O từ một tty và phần còn lại phải tiếp tục như nền quy trình hoặc bạn sẽ thấy tín hiệu điều khiển công việc bắt đầu bị ném (ví dụ: SIGTTIN/SIGTTOU).

Trong việc thực hiện các kịch bản Python của bạn, tôi tin rằng những điều sau đây xảy ra:

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

Tôi đã kiểm tra ra FreeBSD manual for its top implementation và tôi thấy khẩu súng hút thuốc sau:

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. 

Trong khi top không biết rằng nó đang được chạy trong tiến trình nền (việc xử lý tệp đang được thực hiện với trình quản lý ngữ cảnh Python) và bạn đã không chỉ định chế độ không tương tác, giả sử rằng nó miễn phí để sử dụng tty - có nghĩa là bạn có thể S ee SIGTTIN báo hiệu nếu top nhận được tín hiệu của bất kỳ tín hiệu STDIN và SIGTTOU nào khi lệnh được xử lý và cố gắng cập nhật màn hình.

Quan tâm đặc biệt từ thực hiện đầu FreeBSD, sự khác biệt trong những gì sẽ xảy ra khi gọi một cách tương tác hay không:

Ý tưởng của bạn để thêm shell=True thẩm tra lý thuyết này là nó sets the child process of 'top' to the PID of the shell that subprocess.Popen(..) spawns, mà vẫn còn trong một nền tảng Chuỗi Python.

(xin lỗi n.b.: Tôi không có quyền truy cập đến một máy chủ FreeBSD 8.1 để xác minh hành vi trên OS host của bạn ngay bây giờ.)