异步输入输出允许在执行输入输出操作的同时并发地执行原始任务。在任务与I/O操作在逻辑上是相互独立的情况下,可以使用AIO将I/O操作从任务中分离出来。VxWorks的AIO实现符合POSIX1003.1标准。
AIO的可以带来更高的处理效率:它允许在资源可用时进行I/O操作,而不用等待相关事件完成。AIO避免了 同步I/O中的一些不必要的任务阻塞,从而减少了I/O与内部处理之间对资源的竞争,同时也提高了吞吐量。
VxWorks配置
为了使用AIO,需要添加INCLUDE_POSIX_AIO和INCLUDE_POSIX_AIO_SYSDRV组件。第二个组件使能了辅助AIO系统驱动,当前所有的VxWorks设备都需要这个驱动以进行AIO操作。
POSIX AIO函数
VxWorks的aioPxLib库提供了POSIX AIO函数。为了异步访问一个文件,可以使用open()函数打开该文件。然后使用open()返回的文件描述符调用AIO函数。POSIX AIO函数(包括两个非POSIX函数)如下表所示:
当VxWorks配置了INCLUDE_POSIX_AIO之后,内核初始化代码将自动条用aioPxLibInit()函数。aioPxInit()函数使用MAX_LIO_CALLS作为其唯一参数。当MAX_LIO_CALLS配置参数被设置为0(默认为0),将会使用AIO_CLUST_MAX宏,该宏的值在privateaioPxLibP.h中定义为100。
当在VxWorks中同时使用了INCLUDE_POSIX_AIO和INCLUDE_POSIX_AIO_SYSDRV组件时,内核函数aioSysInit()函数将自动初始化AIO系统驱动aioSysDrv。aioSysDrv驱动提供了与任何特定设备驱动相独立的请求队列,因此任意VxWorks设备驱动都可以使用AIO。AioSysInit()函数需要有三个参数:待创建的AIO系统任务数、任务优先级、任务栈大小。AIO系统任务数与能够并行处理AIO请求的数量相同。aioSysInit()函数的三个参数由VxWorks的配置参数MAX_AIO_SYS_TASK、AIO_TASK_PRIORTY和AIO_TASK_STACK_SIZE设定。默认情况下,这些配置参数均设置为0,此时实际获取的是宏AIO_IO_TASK_DEFAULT、AIO_IO_PRIO_DELT、AIO_IO_STACK_DELT中定义的值,分别为2,50,0x700。
AIO控制块
每个AIO调用都将使用一个AIO控制块(aiocb)作为参数。调用函数必须为aiocb分配空间,在进行AIO操作的过程中,这些空间必须保持有效(除非调用函数不会再AIO操作完成并调用aio_return()之前返回,否则不应该在任务栈中创建aiocb()。每个aiocb描述了一个AIO操作。因此,同时使用同一个aiocb执行AIO操作是无效的,并且会产生未定义的后果。
aiocb结构体在aio.h中定义,包括如下元素:
- aio_fildes:I/O文件描述符;
- aio_offset:从文件头部的偏移量;
- aio_buf:从AIO中读取或存放数据的缓冲区地址;
- aio_nbytes:读或写的字节数;
- aio_reqprio:为AIO请求所减少的优先级;
- aio_sigevent:操作完成时返回的信号(可选);
- aio_lio_opcode:由lio_listio()函数执行的操作;
- aio_sys:由VxWorks指定的数据地址(非POSIX)。
使用AIO
函数aio_read()、aio_write()、lio_listio()将初始化AIO操作。lio_listio()函数用于设置可以同时进行异步读写请求的数量。通常,并不是在产生AIO请求后就能立刻用这三个函数完成I/O(读写)初始化。因此,它们的返回值不能反映实际的I/O操作结果,仅仅能够说明一个请求是否成功。
可以使用aio_error()和aio_return()函数获取I/O操作是否成功的信息。
带周期检查完成的AIO
如下内核代码使用管道进行AIO操作。示例中创建了一个管道,提交了一个AIO读请求,验证读请求是否还在处理状态中,并提交了一个AIO写请求。在一般情况下,对一个空管道进行同步读的话,将不能够继续执行后续的写操作。但是在AIO中,初始化了读请求后将会继续执行。当写请求提交之后,示例任务将进入循环,周期性地检查AIO请求的状态,直到读写完成为止。因为AIO控制块是在任务栈空间分配的,所以必须在aioExample()函数返回之前调用aio_return()函数。
示例如下:
测试AIO完成的其他方法
一个任务可以通过如下方式确定一个AIO请求是否完成:
- 像前一个示例中一样,周期性地检查aio_error()的返回值,直到一个AIO请求的状态不再是EINPROGRESS。
- 使用aio_suspend()函数将一个任务挂起,直到AIO请求完成。
- 当AIO请求完毕后,使用信号进行通知。
下面的示例内核代码与之前的aioExample()类似,区别在于它使用了信号来通知写操作的完成。如果从shell进行测试,需要使用比AIO系统任务更低的低优先级创建该任务,以免AIO请求被测试函数阻塞。