异步IO(AIO) 网络通信 模型

本文介绍了Linux中的异步I/O(AIO)模型及其API。AIO允许进程发起多个I/O操作而不阻塞,通过aiocb结构管理和识别操作。文章详细讲解了aio_read、aio_error等关键函数的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

http://wenku.baidu.com/view/dae7dbfcc8d376eeaeaa31d6.html

Linux® 中最常用的输入/输出(I/O)模型是同步 I/O。在这个模型中,当请求发出之后,应用程序就会阻塞,直到请求满足为止。这是很好的一种解决方案,因为调用应用程序在等待 I/O 请求完成时不需要使用任何中央处理单元(CPU)。但是在某些情况中,I/O 请求可能需要与其他进程产生交叠。可移植操作系统接口(POSIX)异步 I/OAIO)应用程序接口(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 中,我们发起了一次传输操作,当传输操作完成或发生错误时,系统调用就会返回。

Linux 上的 AIO

AIO 在 2.5 版本的内核中首次出现,现在已经是 2.6 版本的产品内核的一个标准特性了。

    在异步非阻塞 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( "file.txt", 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,就一直忙碌等待,直到状态发生变化为止。现在,请求可能成功,也可能失败。

使用 AIO 接口来编译程序

我们可以在 aio.h头文件中找到函数原型和其他需要的符号。在编译使用这种接口的程序时,我们必须使用 POSIX 实时扩展库(librt)。

    注意使用这个 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的返回值就等价于同步情况中 readwrite系统调用的返回值(所传输的字节数,如果发生错误,返回值就为 -1)。

aio_write

aio_write函数用来请求一个异步写操作。其函数原型如下:

int aio_write( struct aiocb *aiocbp );
      

 

  aio_write函数会立即返回,说明请求已经进行排队(成功时返回值为 0,失败时返回值为 -1,并相应地设置 errno)。

    这与 read系统调用类似,但是有一点不一样的行为需要注意。回想一下对于 read调用来说,要使用的偏移量是非常重要的。然而,对于 write来说,这个偏移量只有在没有设置 O_APPEND选项的文件上下文中才会非常重要。如果设置了 O_APPEND,那么这个偏移量就会被忽略,数据都会被附加到文件的末尾。否则,aio_offset域就确定了数据在要写入的文件中的偏移量。

aio_error(3) - Linux man page

Name

aio_error - get error status of asynchronous I/O operation

Synopsis

#include <aio.h>

int aio_error(const struct aiocb *aiocbp);

Description

The aio_error() function returns the error status for the asynchronous I/O request with control block pointed to by aiocbp.

Return Value

This function returns EINPROGRESS if the request has not been completed yet. It returns ECANCELED if the request was cancelled. It returns 0 if the request completed successfully. Otherwise an error value is returned, the same value that would have been stored in the errno variable in case of a synchronous read, write, fsync, or fdatasync request. On error, the error value is returned.

Errors

EINVAL

aiocbp does not point at a control block for an asynchronous I/O request of which the return status (see aio_return(3)) has not been retrieved yet.

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值