Linux® 中最常用的输入/输出(I/O)模型是同步 I/O。在这个模型中,当请求发出之后,应用程序就会阻塞,直到请求满足为止。这是很好的一种解决方案,因为调用应用程序在等待 I/O 请求完成时不需要使用任何中央处理单元(CPU)。但是在某些情况中,I/O 请求可能需要与其他进程产生交叠。可移植操作系统接口(POSIX)异步 I/O(AIO)应用程序接口(API)就提供了这种功能。在本文中,我们将对这个 API 概要进行介绍,并来了解一下如何使用它。
AIO 简介
Linux 异步 I/O 是 Linux 内核中提供的一个相当新的增强。它是 2.6 版本内核的一个标准特性,但是我们在 2.4 版本内核的补丁中也可以到它。AIO 背后的基本思想是允许进程发起很多 I/O 操作,而不用阻塞或等待任何操作完成。稍后或在接收到 I/O 操作完成的通知时,进程就可以检索 I/O 操作的结果。
Linux 上的 AIO 简介
本节将探索 Linux 的异步 I/O 模型,从而帮助我们理解如何在应用程序中使用这种技术。
在传统的 I/O 模型中,有一个使用惟一句柄标识的 I/O 通道。在 UNIX® 中,这些句柄是文件描述符(这对等同于文件、管道、套接字等等)。在阻塞 I/O 中,我们发起了一次传输操作,当传输操作完成或发生错误时,系统调用就会返回。
| |||
在异步非阻塞 I/O 中,我们可以同时发起多个传输操作。这需要每个传输操作都有惟一的上下文,这样我们才能在它们完成时区分到底是哪个传输操作完成了。在 AIO 中,这是一个 aiocb(AIO I/O Control Block)结构。这个结构包含了有关传输的所有信息,包括为数据准备的用户缓冲区。在产生 I/O (称为完成)通知时,aiocb 结构就被用来惟一标识所完成的 I/O 操作。这个 API 的展示显示了如何使用它。
AIO API
AIO 接口的 API 非常简单,但是它为数据传输提供了必需的功能,并给出了两个不同的通知模型。表 1 给出了 AIO 的接口函数,本节稍后会更详细进行介绍。
表 1. AIO 接口 API
API 函数 | 说明 |
aio_read | 请求异步读操作 |
aio_error | 检查异步请求的状态 |
aio_return | 获得完成的异步请求的返回状态 |
aio_write | 请求异步写操作 |
aio_suspend | 挂起调用进程,直到一个或多个异步请求已经完成(或失败) |
aio_cancel | 取消异步 I/O 请求 |
lio_listio | 发起一系列 I/O 操作 |
每个 API 函数都使用 aiocb 结构开始或检查。这个结构有很多元素,但是清单 1 仅仅给出了需要(或可以)使用的元素。清单 1. aiocb 结构中相关的域
struct aiocb { int aio_fildes; // File Descriptor int aio_lio_opcode; // Valid only for lio_listio (r/w/nop) volatile void *aio_buf; // Data Buffer size_t aio_nbytes; // Number of Bytes in Data Buffer struct sigevent aio_sigevent; // Notification Structure /* Internal fields */ ... }; |
sigevent 结构告诉 AIO 在 I/O 操作完成时应该执行什么操作。我们将在 AIO 的展示中对这个结构进行探索。现在我们将展示各个 AIO 的 API 函数是如何工作的,以及我们应该如何使用它们。
清单 1. aiocb 结构中相关的域
struct aiocb { int aio_fildes; // File Descriptor int aio_lio_opcode; // Valid only for lio_listio (r/w/nop) volatile void *aio_buf; // Data Buffer size_t aio_nbytes; // Number of Bytes in Data Buffer struct sigevent aio_sigevent; // Notification Structure /* Internal fields */ ... }; |
sigevent 结构告诉 AIO 在 I/O 操作完成时应该执行什么操作。我们将在 AIO 的展示中对这个结构进行探索。现在我们将展示各个 AIO 的 API 函数是如何工作的,以及我们应该如何使用它们。
aio_read
aio_read 函数请求对一个有效的文件描述符进行异步读操作。这个文件描述符可以表示一个文件、套接字甚至管道。aio_read 函数的原型如下:
int aio_read( struct aiocb *aiocbp ); |
aio_read 函数在请求进行排队之后会立即返回。如果执行成功,返回值就为 0;如果出现错误,返回值就为 -1,并设置 errno 的值。
要执行读操作,应用程序必须对 aiocb 结构进行初始化。下面这个简短的例子就展示了如何填充 aiocb 请求结构,并使用 aio_read 来执行异步读请求(现在暂时忽略通知)操作。它还展示了 aio_error 的用法,不过我们将稍后再作解释。
清单 2. 使用 aio_read 进行异步读操作的例子
#include <aio.h> ... int fd, ret; struct aiocb my_aiocb; fd = open( "", O_RDONLY ); if (fd < 0) perror("open"); /* Zero out the aiocb structure (recommended) */ bzero( (char *)&my_aiocb, sizeof(struct aiocb) ); /* Allocate a data buffer for the aiocb request */ my_aiocb.aio_buf = malloc(BUFSIZE+1); if (!my_aiocb.aio_buf) perror("malloc"); /* Initialize the necessary fields in the aiocb */ my_aiocb.aio_fildes = fd; my_aiocb.aio_nbytes = BUFSIZE; my_aiocb.aio_offset = 0; ret = aio_read( &my_aiocb ); if (ret < 0) perror("aio_read"); while ( aio_error( &my_aiocb ) == EINPROGRESS ) ; if ((ret = aio_return( &my_iocb )) > 0) { /* got ret bytes on the read */ } else { /* read failed, consult errno */ } |
在清单 2 中,在打开要从中读取数据的文件之后,我们就清空了 aiocb 结构,然后分配一个数据缓冲区。并将对这个数据缓冲区的引用放到 aio_buf 中。然后,我们将 aio_nbytes 初始化成缓冲区的大小。并将 aio_offset 设置成 0(该文件中的第一个偏移量)。我们将 aio_fildes 设置为从中读取数据的文件描述符。在设置这些域之后,就调用 aio_read 请求进行读操作。我们然后可以调用 aio_error 来确定 aio_read 的状态。只要状态是 EINPROGRESS,就一直忙碌等待,直到状态发生变化为止。现在,请求可能成功,也可能失败。
| |||
注意使用这个 API 与标准的库函数从文件中读取内容是非常相似的。除了 aio_read 的
一些异步特性之外,另外一个区别是读操作偏移量的设置。在传统的 read 调用中,偏移量是在文件描述符上下文中进行维护的。对于每个读操作来说,偏移量都需要进行更新,这样后续的读操作才能对下一块数据进行寻址。对于异步 I/O 操作来说这是不可能的,因为我们可以同时执行很多读请求,因此必须为每个特定的读请求都指定偏移量。
aio_error
aio_error 函数被用来确定请求的状态。其原型如下:
int aio_error( struct aiocb *aiocbp ); |
这个函数可以返回以下内容:
∙ EINPROGRESS,说明请求尚未完成
∙ ECANCELLED,说明请求被应用程序取消了
∙ -1,说明发生了错误,具体错误原因可以查阅 errno
∙ 0 说明操作成功
aio_return
异步 I/O 和标准块 I/O 之间的另外一个区别是我们不能立即访问这个函数的返回状态,因为我们并没有阻塞在 read 调用上。在标准的 read 调用中,返回状态是在该函数返回时提供的。但是在异步 I/O 中,我们要使用 aio_return 函数。这个函数的原型如下:
ssize_t aio_return( struct aiocb *aiocbp ); |
只有在 aio_error 调用确定请求已经完成(可能成功,也可能发生了错误)之后,才会调用这个函数。aio_return 的返回值就等价于同步情况中 read 或 write 系统调用的返回值(所传输的字节数,如果发生错误,返回值就为 -1)。
aio_write
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论