异步I/O之用户空间

原创 2012年03月30日 17:34:24

Linux2.6异步I/O

 

AIO的基本思想:
  允许进程发起很多I/O操作,而不用阻塞或等待任何操作完成,稍后或在
  接收到I/O操作完成通知时,进程可以检索I/O操作结果
  
  在异步非阻塞I/O中,我们可以同时发起多个传输操作,这需要每个传输操作都有唯一的上下文
  ,这样我们才能在他们完成时区分到底是哪个传输操作完成了,这个
  工作可以通过aiocb结构体进行区分
  
  其中,struct aiocb主要包含以下字段:
   int               aio_fildes;        /* 要被读写的fd */
   void *            aio_buf;           /* 读写操作对应的内存buffer */
   __off64_t         aio_offset;        /* 读写操作对应的文件偏移 */
   size_t            aio_nbytes;        /* 需要读写的字节长度 */
   int               aio_reqprio;       /* 请求的优先级 */
   struct sigevent   aio_sigevent;      /* 异步事件,定义异步操作完成时的通知信号或回调函数 */
  
  

 

 
 
 异步阻塞I/O的思想:
  一个典型的例子就是select调用,具体请看下图


  
  
  
  
  
  
  
 异步非阻塞I/O
  在一个进程中为了执行多个I/O请求而对计算操作和I/O处理进行重叠处理的能力,利用了处理速度和I/O速度之间的差异。
  当一个或多个I/O请求挂起时,CPU可以执行其他任务
  具体见图
 
 
 
 
 
 
 AIO相关操作
 
 用户空间:
 1、aio_read
  请求对一个有效的文件描述符进行异步读操作,这个文件描述符可以表示一个文件、套接字甚至管道
  int aio_read(struct aiocb *aiocbp);
  aio_read函数在请求进行排队之后会立即返回,执行成功返回0,出错返回-1,并设置errno的值
  
  
 2、aio_write
  aio_write函数用来请求一个异步的写操作
  int aio_write(struct aiocb *aiocbp);
  aio_write函数会立即返回,具体与aio_read相同
  
  
 3、aio_error
  被用来确定请求状态
  int aio_error(struct aiocb * aiocbp);
  这个函数可以返回以下内容
  EINPROGRESS:说明请求尚未完成
  ECANCELLED:说明请求被应用程序取消
 4、aio_return
  异步I/O和标准I/O方式之间的另外一个区别是不能立即访问这个函数的返回状态,因为
  异步I/O并没有阻塞在read()调用上,在标准的read调用中返回状态时在该函数返回时提供的
  。但是在异步I/O中,我们要使用aio_return函数
  ssize_t aio_return(struct aiocb *aiocbp);
 
 
  此函数只有在aio_error调用确定请求已经完成之后才会调用这个函数
  
  
  
  /*用户空间异步读例程*/
  #include <aio.h>
  ...
  int fd,ret;
  struct aiocb my_aiocb;
  
  
  fd = open("file.txt",O_RDONLY);
  if(fd<0)
   perror("open");
   
   
  /*清零aiocb结构体*/
  bzero((char *)&my_aiocb,sizeof(struct aiocb));

  /*为aiocb请求分配数据缓冲区*/
  my_aiocb.aio_buf = malloc(BUFSIZE+1);
  if(!my_aiocb.aio_buf)
   perror("malloc");
   
  /*初始化aiocb的成员*/
  my_Aiocb.aio_filds = 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) == EINPROCESS)
   continue;
  
  
  if((ret = aio_return(&my_iocb))>0){
   /*获得异步读的返回值*/
  }else{
   /*读失败,分析errorno*/
  }
 
 
 
 5.aio_suspend
  用户可以使用aio_suspend()函数来挂起或阻塞掉用进程,直到异步请求完成为止,此时产生一个信号
  ,或者在发生其他超市操作时会导致aio_suspend()返回
  int aio_suspend(const struct aiocb *const cblist[],
       int n,const struct timespec *timeout);
  
  此函数在用户空间的使用例程:
  struct aioct *cblist[MAX_LIST]
  /*清空aioct结构体链表*/
  bzero((char *)cblist,sizeof(cblist));
  /*将一个或更多的aiocb放入aioct结构体链表*/
  cblist[0] = &my_aiocb;
  ret = aio_read(&my_aiocb);
  ret = aio_suspend(cblist,MAX_LIST,NULL);
 
 
 
 6.aio_cancel
  允许用户取消对某个文件描述符执行的一个或所有的I.O请求
  int aio_cancel(int fd,struct aiocb *aiocbp);
  //用户需要提供一个aiocb指针,如果这个请求被成功取消,那么这个函数就会返回
  AIO_CANCELED,如果请求完成,此函数就会返回AIO_TCANCELED
  aiocbp参数设置为NULL,如果所有的请求都被取消了,这个函数将返回
  AIO_CANCELED,如果至少有一个请求被取消,那么这个函数就会返回AIO_NOT_CANCELED
  如果一个也没被取消就会返回AIO_ALLODNOE
  
  
 7.lio_listio
  listio函数可以用于同时发起多个请求(很重要),它使得用户可以在
  一个系统调用中启动大量的I/O操作。
  int lio_listio(int mode,struct aiocb *list[],int nent,struct sigevent *sig);
  参数介绍:
   mode参数可以是LIO_WAIT或LIO_NOWAIT。
   LIO_WAIT会阻塞这个调用,直到所有的I/O都完成为止
   LIO_NOWAIT立即返回
   list是aiocb的引用列表
   nent最大元素个数
  
  
  此函数的使用里程:
  struct aiocb aiocb1,aiocb2;
  struct aiocb *list[MAX_LIST];
  
  ...
  /*准备第一个aiocb*/
  aiocb1.aio_fields = fd;
  aiocb1.aio_buf = malloc(BUFSIZE+1);
  aiocb1.aio_nbytes = BUFSIZE;
  aiocb1.aio_offset = next_offset;
  aiocb1.aio_lio_opcode = LIO_READ;/*异步读操作,写操作为LIO_WRITE\
  空操作为LIO_NOP*/
  .../*贮备多个aiocb*/
  bzero((char *)'list,sizeof(list));
  
  /*将aiocb填入链表*/
  list[0] = &aiocb1;
  list[1] = &aiocb2;
  ...
  ret = lio_listio(LIO_WAIT,list,MAX_LIST,NULL);
  
  
  
  
  
 
 
下面接收两种作为AIO通知的方法
 一.使用信号作为AIO的通知
  此时使用AIO的应用程序同样需要定义信号处理函数,在指定的信号被产生时
  会触发这个处理程序,而aiocb被提供给信号处理函数用来区分AIO请求
  
  /*例程*/
  void setup_io(...)
  {
   int fd;
   struct sigaction sig_act;
   struct aiocb my_aiocb;
   ...
   /*设置信号处理函数*/
   sigemptyset(&sig_act.sa_mask);
   sig_act.sa_flags = AS_SIGINFO;
   sig_act.sa_sigaction = aio_completion_handler;
   
   /*设置AIO请求*/
   bzero((char*)&my_aiocb,sizeof(struct aiocb));
   my_aiocb.aio_files = fd;
   my_aiocb.aio_buf = malloc(BUF_SIZE + 1);
   my_aiocb.aio_nbytes = BUF_SIZE;
   my_aiocb.aio_offset = next_offset;
   
   
   /*连接AIO请求和信号处理函数(通过信号SIGIO)*/
   my_aiocb.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
   my_aiocb.aio_sigevent.sigev_signo = SIGIO;
   my_aiocb.aio_sigevent.sigev_value.sival_ptr = &my_aiocb;
   
   /*将信号与信号处理函数绑定*/
   ret = sigaction(SIGIO,&sig_act,NULL);
   ...
   ret = aio_read(&my_aiocb);/*发出异步读请求*/
   
  }
  
  
  /*信号处理函数*/
  void aio_completion_handler(int signo,siginfo_t *info,void *context)
  {
   struct aiocb *req;
   
   
   /*确定是我们需要的信号*/
   if(info->si_signo == SIGIO)
   { 
    req = (struct aiocb*)info->si_value.sival_ptr;/*获得aiocb*/
   
   
    /*请求的操作完成了吗?*/
    if(aio_error(req)==0){
     /*请求的操作完成,获取返回值*/
     ret = aio_return(req);
    }
   }
  }
  
  
 二.使用回调函数作为AIO的通知
  /*设置异步I/O请求*/
  void setup_io(...)
  {
   int fd;
   struct aiocb my_aiocb;
   
   ...
   /*设置AIO请求*/
   bzero((char*) &my_aiocb,sizeof(struct aiocb));
   my_aiocb.aio_fildes = fd;
   my_aiocb.aio_buf = malloc(BUF_SIZE + 1);
   my_aiocb.aio_nbytes = BUF_SIZE;
   my_aiocb.aio_offset = next_offset;
   
   /*连接AIO请求和线程回调函数*/
   my_aiocb.aio_sigevent.sigev_notify = SIGEV_THREAD;
   my_aiocb.aio_sigevent.notify_function = aio_completion_handler;
   /*设置回调函数*/
   my_aiocb.aio_sigevent.notify_attributes = NULL;
   my_aiocb.aio_sigevent.sigev_value.sigval_ptr = &my_aiocb;
   ...
   ret = aio_read(&my_aiocb);/*发起AIO请求*/
   
  }
  
  
  /*异步I/O完成回调函数*/
  void aio_completion_handler(sigval_t sigval)
  {
   struct aiocb*req;
   req = (struct aiocb*)sigval_ptr;
   
   
   /*AIO请求完成*/
   if(aio_error(req) == 0)
   {
    /*请求完成,获得返回值*/
    ret = aio_return(req);
   }
  }
  
  //历程解析:上述程序在创建aiocb、请求之后,使用SIGEV_THREAD请求一个线程回调函数
  来作为通知方法,在回调函数中,通过(struct aiocb*)sigval.sival_ptr可以获得对应的aiocb指针,使用
  AIO函数验证请求是否完成

 

 

/**************************************************最后附上我所做的实验代码*******************************************************/

/************************使用回调函数的异步I/O**************************/
 

#include <aio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
 
void async_read(sigval_t val)
{
 
struct aiocb *ptr = (struct aiocb *)val.sival_ptr;
printf("read=%s", (char *)ptr->aio_buf);
}
 
int main(void)
{
 int fd;
 struct aiocb cb;
 char sbuf[100];
 int ret;


 fd = open("hello",O_RDWR,S_IRUSR|S_IWUSR);
 bzero(&cb, sizeof(cb));
 cb.aio_fildes = fd;
 cb.aio_buf = sbuf;
 cb.aio_nbytes = 100;
 cb.aio_offset = 0;
 cb.aio_sigevent.sigev_value.sival_ptr = &cb;
 
 
 /*设置回调函数*/
 cb.aio_sigevent.sigev_notify = SIGEV_THREAD;
 cb.aio_sigevent.sigev_notify_function = async_read;
 cb.aio_sigevent.sigev_notify_attributes = NULL;

 
 //发送异步读请求
 ret = aio_read(&cb);
 
 if (ret == -1) {
 perror("aio_read");
 exit(1);
 }
 int i = 0;
 while (1) {
  printf("%d\n",i++);
  sleep(1);
 }
return 0;
}

执行结果如下图所示


 

 

/*****************************使用信号通知的异步IO**********************************/
 

/*使用信号作为AIO的通知*/

#include <aio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>

 
 //信号处理函数
void async_read(int s, siginfo_t * info, void * context)
{
 struct aiocb *ptr = (struct aiocb *)info->si_value.sival_ptr;
 printf("read=%s", (char *)ptr->aio_buf);
}

int main(void)
{

 struct aiocb cb;
 int fd;
 char sbuf[100];
 int ret;
 struct sigaction act;


 fd = open("hello",O_RDWR,S_IRUSR|S_IWUSR);
 sigemptyset(&act.sa_mask);
 act.sa_flags = SA_RESTART | SA_SIGINFO;
 act.sa_sigaction = async_read;
 sigaction(SIGIO, &act, NULL);
 bzero(&cb, sizeof(cb));

 cb.aio_fildes = fd;
 cb.aio_buf = sbuf;
 cb.aio_nbytes = 100;
 cb.aio_offset = 0;


 cb.aio_sigevent.sigev_value.sival_ptr = &cb;

 cb.aio_sigevent.sigev_notify = SIGEV_SIGNAL;

 cb.aio_sigevent.sigev_signo = SIGIO;

 ret = aio_read(&cb);

 if (ret == -1) {

 perror("aio_read");

 exit(1);

 }

 int i = 0;

 while (1) {
 printf("%d\n",i++);
 sleep(3);
 }

return 0;
}

 

执行结果如下:


  
 
 
 
 
 
 最后需要注意的是再编译aio.c的时候

 要加相应的库,-lrt

gcc -o test aio_signal.c -lrt

 
 
 
 
 
 
 
 
 
 
 
 
 
 

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

122-POSIX 异步IO(aiocb)

1. 引言异步 IO 就异步 IO 嘛,为什么还要加个 POSIX。如同前面我学过的进程间通信一样,有很多版本,有 System V 的函数,也有 POSIX 定义的函数,他们只是使用不同的函数未完成...

Linux AIO机制

Linux的I/O机制经历了一下几个阶段的演进:1. 同步阻塞I/O: 用户进程进行I/O操作,一直阻塞到I/O操作完成为止。2. 同步非阻塞I/O: 用户程序可以通过设置文件描述符的属性O_NONB...
  • tq02h2a
  • tq02h2a
  • 2009年01月18日 10:24
  • 15299

同步异步阻塞非阻塞I/O思维导图

  • 2012年11月05日 21:34
  • 355KB
  • 下载

网络聊天室(Select异步I/O模型)

  • 2011年02月14日 16:52
  • 8.48MB
  • 下载

c++实现半同步半异步I/O的设计模式(half sync/half async)

半同步半异步I/O的设计模式(half sync/half async) 1.动机: 众所周知,同步模式编程简单,但是I/O的利用利率低;而异步模式编程复杂,但是I/O利用率高。 综合同步异步的...

socket阻塞与非阻塞,同步与异步、I/O模型

socket阻塞与非阻塞,同步与异步 作者:huangguisu 1. 概念理解      在进行网络编程时,我们常常见到同步(Sync)/异步(As...

信号驱动(SIGIO)的异步I/O

ocket信号驱动 第一条:概述  信号驱动IO,不是异步IO。 信号驱动IO是指:进程预先告知内核,使得 当某个socketfd有events(事件)发生时,内核使...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:异步I/O之用户空间
举报原因:
原因补充:

(最多只允许输入30个字)