Linux AIO API 包装器
项目描述
内容
</nav>Linux AIO API 包装器
这是关于内核内、基于文件描述符的异步 I/O。它与asyncio标准模块无关。
Linux AIO 入门
在发送或期待数据时,开发人员面临的典型问题是知道操作何时完成,以便程序可以继续。
读/写/接收/发送:阻塞直到事情发生
同样,在非阻塞文件描述符上:错误输出而不是阻塞,开发人员必须以某种方式实现重试,并且可能最终浪费CPU时间,只是一遍又一遍地重新提交相同的操作。
select/poll/epoll:内核告诉程序何时(重新)提交操作不应该阻塞(如果开发人员小心不要有竞争的 IO 源)
AIO 是下一个级别:应用程序表达了当文件描述符接受它并为内核提供相应的缓冲区时发生某些 IO 操作的意图。与 select/poll/epoll 相比,当操作变为可能时,这避免了到用户空间的一次往返:
内核发送通知(例如:fd 可读)
程序启动实际 IO(例如:从 fd 读取)
相反,内核只需要通知用户空间操作已经完成,应用程序可以处理接收到的数据,或者提交更多数据发送。
边缘案例
由于这种高级别的集成,由高开销 API 抽象的低级实现约束可能会变得很明显。
例如,当向 USB 小工具端点文件提交 AIO 块时,该块应与页面边界对齐,因为某些 USB 设备控制器没有读取/写入部分页面的能力。
在 python 中,这意味着应该使用mmap来分配这样的缓冲区,而不仅仅是任何bytearray。
实现细节出现的另一个地方是完成状态 res和res2。它们的含义取决于对使用的文件描述符的模块处理操作,因此 python-libaio 传输这些值而不假设它们的含义(而不是,比如说,在负值上引发)。
还有一个地方是应用程序发起的闭包:取消 AIO 块时存在基本的竞争条件(可能首先发生硬件触发的完成,或者可能是软件发起的取消)。在任何情况下,都会产生一个完成事件,并且应用程序可以检查哪个来源获胜。这样做的结果是 AIO 上下文关闭可能需要时间:虽然请求取消不会阻塞,但软件应该等待硬件将缓冲区交还。
蟒蛇2笔记
在 python 2.7 中,字节数组的内存视图尽管是可写的,但被 ctypes 拒绝:
>>> from ctypes import c_char
>>> a = bytearray(b'foo')
>>> c_char.from_buffer(a)
c_char('f')
>>> b = memoryview(a)
>>> b.readonly
False
>>> c_char.from_buffer(b)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: expected a writeable buffer object
这意味着不可能只在大缓冲区的开头读取或写入几个字节而不必复制内存。
相同的代码适用于 python 3.x 。
这被认为是 python 2.7 ctypes 或 memoryview 错误,而不是 python-libaio 错误。
此外,memoryview 拒绝使用 mmap 对象:
>>> import mmap
>>> a = mmap.mmap(-1, 16*1024)
>>> b = memoryview(a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: cannot make memory view because object does not have the buffer interface
>>>
…但是 ctypes 对此很满意:
>>> import ctypes
>>> c = (ctypes.c_char * len(a)).from_buffer(a)
>>>
…并且 memoryview 接受在 ctype 对象上构造:
>>> d = memoryview(c)
>>>
......它真的有效!
>>> a[0]
'\x00'
>>> c[0]
'\x00'
>>> d[0]
'\x00'
>>> d[0] = '\x01'
>>> c[0]
'\x01'
>>> a[0]
'\x01'
>>> a[0] = '\x02'
>>> c[0]
'\x02'
>>> d[0]
'\x02'
这被认为是 python 2.7 memoryview 或 mmap 错误。