我io_add_watch
在 python 中使用监视器时遇到了问题(通过 gobject)。我想在每次通知后对整个缓冲区进行非阻塞读取。这是代码(稍微缩短了一点):
class SomeApp(object):
def __init__(self):
# some other init that does a lot of stderr debug writes
fl = fcntl.fcntl(0, fcntl.F_GETFL, 0)
fcntl.fcntl(0, fcntl.F_SETFL, fl | os.O_NONBLOCK)
print "hooked", gobject.io_add_watch(0, gobject.IO_IN | gobject.IO_PRI, self.got_message, [""])
self.app = gobject.MainLoop()
def run(self):
print "ready"
self.app.run()
def got_message(self, fd, condition, data):
print "reading now"
data[0] += os.read(0, 1024)
print "got something", fd, condition, data
return True
gobject.threads_init()
SomeApp().run()
这是诀窍 - 当我在没有激活调试输出的情况下运行程序时,我没有got_message
接到电话。当我先向stderr写很多东西时,问题就消失了。如果除了这段代码中可见的打印之外我不写任何东西,我就不会收到 stdin 消息信号。另一个有趣的事情是,当我尝试在启用了 stderr 调试但通过strace
(检查是否有任何我错过的 fcntl / ioctl 调用)的情况下运行相同的应用程序时,问题再次出现。
简而言之:如果我先在没有 strace 的情况下向 stderr 写了很多东西,那就io_watch
可以了。如果我用 strace 写了很多,或者根本不写都io_watch
行不通。
“其他一些初始化”部分需要一些时间,所以如果我在看到“钩子 2”输出之前输入一些文本,然后在“就绪”之后按“ctrl+c”,则get_message
调用回调,但读取调用会抛出 EAGAIN,所以缓冲区似乎是空的。
与标准输入相关的 strace 日志:
ioctl(0, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(0, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
fcntl(0, F_GETFL) = 0xa002 (flags O_RDWR|O_ASYNC|O_LARGEFILE)
fcntl(0, F_SETFL, O_RDWR|O_NONBLOCK|O_ASYNC|O_LARGEFILE) = 0
fcntl(0, F_GETFL) = 0xa802 (flags O_RDWR|O_NONBLOCK|O_ASYNC|O_LARGEFILE)
有人对这里发生的事情有一些想法吗?
编辑:另一个线索。我试图重构应用程序以在不同的线程中进行读取并通过管道将其传回。它“有点”有效:
...
rpipe, wpipe = os.pipe()
stopped = threading.Event()
self.stdreader = threading.Thread(name = "reader", target = self.std_read_loop, args = (wpipe, stopped))
self.stdreader.start()
new_data = ""
print "hooked", gobject.io_add_watch(rpipe, gobject.IO_IN | gobject.IO_PRI, self.got_message, [new_data])
def std_read_loop(self, wpipe, stop_event):
while True:
try:
new_data = os.read(0, 1024)
while len(new_data) > 0:
l = os.write(wpipe, new_data)
new_data = new_data[l:]
except OSError, e:
if stop_event.isSet():
break
time.sleep(0.1)
...
令人惊讶的是,如果我只是将相同的文本放入新管道中,一切都会开始工作。问题是:
- 第一行根本没有“注意到” - 我只得到第二行和以下行
- 丑陋
也许这会给其他人一个关于为什么会发生这种情况的线索?