31.2. 分散/聚集 I/O
分散/聚集I/O涉及在I/O API和客户代码之间,以物理离散的方式交换信息。在所有我曾遇到的情况中,所谓的离散就是一组分离的内存块。以UNIX的readv()和writev()函数为例,这两个函数的行为类似于其同胞read()和write(),但read()和write()的参数是指向单个内存区域的指针,以及区域的大小,而readv()和writev()则需要传入iovec结构的数组:
struct iovec { void* iov_base; size_t iov_len; }; ssize_t readv(int fd, const struct iovec* vector, int count); ssize_t writev(int fd, const struct iovec* vector, int count); Windows Sockets API也有类似的结构和对应的函数: struct WSABUF { u_long len; char* buf; }; int WSARecv(SOCKET s , WSABUF* lpBuffers , DWORD dwBufferCount , . . . // And 4 more parameters); int WSASend(SOCKET s , WSABUF* lpBuffers , DWORD dwBufferCount , . . . // And 4 more parameters); int WSARecvFrom(SOCKET s , WSABUF* lpBuffers , DWORD dwBufferCount , . . . // And 6 more parameters); int WSASendTo( SOCKET s , WSABUF* lpBuffers , DWORD dwBufferCount , . . . // And 6 more parameters);
|
你可能想知道,既然这种I/O方式将显著增加客户代码的复杂度,为什么人们还要采用呢?理由如下,首先如果你的文件或者网络数据采用固定格式,你就可以一次对一条或者多条记录/包执行I/O,而无需将数据来回移动、重新格式化,或者彼此拼接,这有可能带来很大的便利。与此类似,如果你的记录/包的格式可变,而首部的大小固定,你可以用匹配的结构直接读写首部,然后把剩余部分当作长度可变的不透明内存块。还有第三个原因:性能。我曾经设计了一个服务器架构,其中就使用了分散/聚集I/O,而后者又使用多线程无阻塞的内存分配方案。(毫不夸张地讲,这个架构性能优异。)
但是不论对提高性能有多大帮助,使用分散/聚集I/O是有代价的,一旦处理变长的记录/包,或者记录/包的有效载荷中包含变长元素,客户代码就变得很复杂,透明性也大多不好,还容易出错。因此一个高效的抽象必不可少。
【责任编辑:
董书 TEL:(010)68476606】