我正在尝试将程序从Tornado转换为Asyncio,第一步是将实际的asyncio eventloop用作described here。
这个应用程序在嵌入式Linux机器上运行,我通过sysfs/gpio subsystem使用GPIO,而在其中一些GPIO上我正在等待中断。通过执行以下操作,我能够将其直接集成到Tornado IOLoop中:
# Register with the queue
self.io_loop.add_handler(
self.gpio._value_file, self._handle_interrupt, self.io_loop._EPOLLPRI | self.io_loop.ERROR
)
在代码段中,_value_file
是可以从中读取GPIO的文件的文件句柄。只要该GPIO上的中断可用,就会触发事件EPOLLPRI。在龙卷风上,这非常有效。在中断到来之后不久就会调用_handle_interrupt
函数。
我的问题是,我无法将其转换为本机asyncio事件循环。在the documentation for watching file descriptors中我只找到添加读者和编写者的函数,但没有什么可以在文件描述符上查看通用事件掩码。我无法深入研究代码,因为它进入了C.但是,看看Tornado层将Tornado IOLoop中的调用转换为asyncio IOLoop,看起来就是这样:
def add_handler(self, fd, handler, events):
fd, fileobj = self.split_fd(fd)
if fd in self.handlers:
raise ValueError("fd %s added twice" % fd)
self.handlers[fd] = (fileobj, stack_context.wrap(handler))
if events & IOLoop.READ:
self.asyncio_loop.add_reader(
fd, self._handle_events, fd, IOLoop.READ)
self.readers.add(fd)
if events & IOLoop.WRITE:
self.asyncio_loop.add_writer(
fd, self._handle_events, fd, IOLoop.WRITE)
self.writers.add(fd)
仅翻译READ和WRITE标志,忽略所有其他标志。
有人可以确认目前无法使用asyncio来监视除READ和WRITE事件之外的文件描述符上的任何事件吗?或者我做错了什么,实际上还有办法吗?
答案 0 :(得分:3)
我现在已经找到了解决方案。我的主要信息来源是this thread in the Python-tulip group和this piece of code,我必须略微采用。
主要的见解是,可用于监视POLLPRI
事件的epoll本身就是一个文件描述符。每当epoll监视的FD上发生事件时,epoll文件描述符将生成POLLIN
事件,可以使用as add_reader
使用asyncio进行监视。因此,我们不是直接注册,而是手动创建一个epoll结构并将其注册到ioloop,如下所示:
self.epoll = select.epoll()
self.io_loop.add_reader(self.epoll.fileno(), self._handle_interrupt)
然后将实际的中断事件注册到epoll结构
self.epoll.register(self.gpio._value_file, select.POLLPRI)
此时,将在_handle_interrupt
功能中接收中断事件。确保实际轮询事件处理程序中的epoll结构,否则它将不断生成读取事件
def _handle_interrupt(self):
self.epoll.poll(0)
...
使用低级select
功能而不是高级selectors
非常重要,因为它们执行与asyncio
中类似的事件标记过滤。剪切的以下代码来自selectors.EpollSelector
:
def register(self, fileobj, events, data=None):
key = super().register(fileobj, events, data)
epoll_events = 0
if events & EVENT_READ:
epoll_events |= select.EPOLLIN
if events & EVENT_WRITE:
epoll_events |= select.EPOLLOUT
try:
self._epoll.register(key.fd, epoll_events)
except BaseException:
super().unregister(fileobj)
raise
return key
可以看出,除了READ和WRITE之外的所有事件都被过滤掉了。因此,您无法使用高级接口来监视POLLPRI事件。因此,请使用低级接口。
我希望这能帮助人们解决这个问题。