九鼎创展论坛中文版English
登录 | 立即注册 设为首页收藏本站 切换到宽版
查看: 4160|回复: 0
打印 上一主题 下一主题

x4412&ibox项目实战41-Linux设备驱动之异步I/O实验

[复制链接]
跳转到指定楼层
楼主
发表于 2014-10-12 12:11:43 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
Linux中最常用的输入/输出模型是同步I/O。在这个模型中,当请求发出之后,应用程序就会阻塞,直到请求满足为止。这是很好的一种解决方案,因为调用应用程序在等待I/O 请求完成时不需要使用任何中央处理单元。但是在某些情况中,I/O请求可能需要与其他进程产生交叠。可移植操作系统接口异步I/O(AIO)应用程序接口就提供了这种功能。
1.1.1    AIO简介
Linux异步I/O是Linux内核中提供的一个相当新的增强。它是2.6版本内核的一个标准特性,但是我们在2.4版本内核的补丁中也可以找到它。AIO背后的基本思想是允许进程发起很多I/O操作,而不用阻塞或等待任何操作完成。稍后或在接收到I/O操作完成的通知时,进程就可以检索I/O操作的结果。
1.1.2    I/O模型
在深入介绍AIO API之前,让我们先来探索一下Linux上可以使用的不同I/O模型。这并不是一个详尽的介绍,但是我们将试图介绍最常用的一些模型来解释它们与异步I/O之间的区别。下给出了同步和异步模型,以及阻塞和非阻塞的模型。

每个I/O模型都有自己的使用模式,它们对于特定的应用程序都有自己的优点。本节将简要对其一一进行介绍。
1.1.1    同步阻塞I/O
最常用的一个模型是同步阻塞I/O模型。在这个模型中,用户空间的应用程序执行一个系统调用,这会导致应用程序阻塞。这意味着应用程序会一直阻塞,直到系统调用完成为止。调用应用程序处于一种不再消费CPU而只是简单等待响应的状态,因此从处理的角度来看,这是非常有效的。


上图给出了传统的阻塞I/O模型,这也是目前应用程序中最为常用的一种模型。其行为非常容易理解,其用法对于典型的应用程序来说都非常有效。在调用read系统调用时,应用程序会阻塞并对内核进行上下文切换。然后会触发读操作,当响应返回时(从我们正在从中读取的设备中返回),数据就被移动到用户空间的缓冲区中。然后应用程序就会解除阻塞,即read调用返回。
从应用程序的角度来说,read调用会延续很长时间。实际上,在内核执行读操作和其他工作时,应用程序的确会被阻塞。
1.1.1    同步非阻塞I/O
同步阻塞I/O的一种效率稍低的变种是同步非阻塞 I/O。在这种模型中,设备是以非阻塞的形式打开的。这意味着I/O操作不会立即完成,read操作可能会返回一个错误代码,说明这个命令不能立即满足(EAGAIN或EWOULDBLOCK),如下所示。


  非阻塞的实现是I/O命令可能并不会立即满足,需要应用程序调用许多次来等待操作完成。这可能效率不高,因为在很多情况下,当内核执行这个命令时,应用程序必须要进行忙碌等待,直到数据可用为止,或者试图执行其他工作。正上图所示的一样,这个方法可以引入I/O操作的延时,因为数据在内核中变为可用到用户调用read返回数据之间存在一定的间隔,这会导致整体数据吞吐量的降低。
1.1.1    异步阻塞I/O
  另外一个阻塞解决方案是带有阻塞通知的非阻塞I/O。在这种模型中,配置的是非阻塞 I/O,然后使用阻塞 select 系统调用来确定一个I/O描述符何时有操作。使用select调用可以用来为多个描述符提供通知,而不仅仅为一个描述符提供通知。对于每个提示符来说,我们可以请求这个描述符可以写数据、有读数据可用以及是否发生错误的通知。


  select调用的主要问题是它的效率不是非常高。尽管这是异步通知使用的一种方便模型,但是对于高性能的I/O操作来说不建议使用。
1.1.1    异步非阻塞I/OAIO
异步非阻塞I/O模型是一种处理与I/O重叠进行的模型。读请求会立即返回,说明read 请求已经成功发起了。在后台完成读操作时,应用程序会执行其他处理操作。当read 的响应到达时,就会产生一个信号或执行一个基于线程的回调函数来完成这次I/O处理过程。


在一个进程中为了执行多个I/O请求而对计算操作和I/O处理进行重叠处理的能力利用了处理速度与I/O速度之间的差异。当一个或多个I/O请求挂起时,CPU可以执行其他任务;或者更为常见的是,在发起其他I/O的同时对已经完成的I/O进行操作。
1.1.1    异步I/O的动机
从前面I/O模型的分类中,我们可以看出AIO的动机。这种阻塞模型需要在I/O操作开始时阻塞应用程序。这意味着不可能同时重叠进行处理和I/O操作。同步非阻塞模型允许处理和I/O操作重叠进行,但是这需要应用程序根据重现的规则来检查I/O操作的状态。这样就剩下异步非阻塞I/O了,它允许处理和I/O操作重叠进行,包括I/O操作完成的通知。除了需要阻塞之外,select 函数所提供的功能(异步阻塞I/O)与AIO类似。不过,它是对通知事件进行阻塞,而不是对I/O调用进行阻塞。
1.1.2    AIO接口API
  
API 函数
  
说明
aio_read
请求异步读操作
aio_error
检查异步请求的状态
aio_return
获得完成的异步请求的返回状态
aio_write
请求异步写操作
aio_suspend
挂起调用进程,直到一个或多个异步请求已经完成(或失败)
aio_cancel
取消异步 I/O 请求
lio_listio
发起一系列 I/O 操作
AIO具有两种通知方法,使用回调函数或信号进行异步通知。下面我们将结合实例加深对AIO的理解。
       编写应用程序,使用回调函数的方式进行异步通知,读取当前目录下的某文件,并将读取的内容打印出来。
       ubuntu用户目录或samba目录下新建应用程序x4412-aio-app.c,编辑内容如下:
  1. #include <aio.h>
  2. #include <fcntl.h>
  3. #include <unistd.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <errno.h>
  7. #include <string.h>

  8. #define S_IRUSR 00400
  9. #define S_IWUSR 00200
  10. void async_read(sigval_t val)
  11. {
  12.          int ret;
  13.          struct aiocb *ptr = (struct aiocb *)val.sival_ptr;
  14.          if(aio_error(ptr)==0)//AIO请求完成?
  15.          {
  16.                    //请求完成,获得返回值
  17.                    ret = aio_return(ptr);
  18.                    printf("ret = %d\r\n",ret);
  19.                    printf("read=%s\r\n", (char *)ptr->aio_buf);
  20.          }
  21. }
  22. int main(void)
  23. {
  24.          int fd;
  25.         struct aiocb cb;//API函数通过此结构检查
  26.         char sbuf[100];
  27.         int ret;
  28.          //以可读写的方式打开文件,同时应用对该文件也可读写
  29.         fd = open("x4412",O_RDWR,S_IRUSR|S_IWUSR);
  30.         bzero(&cb, sizeof(cb));//清空aiocb结构
  31.         cb.aio_fildes = fd;//指向hello文件
  32.         cb.aio_buf = sbuf;
  33.         cb.aio_nbytes = 100;
  34.         cb.aio_offset = 0;//设置文件偏移量为0
  35.         /*设置回调函数*/
  36.         cb.aio_sigevent.sigev_notify = SIGEV_THREAD;//请求一个线程回调函数
  37.         cb.aio_sigevent.sigev_notify_function = async_read;//连接线程回调函数
  38.         cb.aio_sigevent.sigev_notify_attributes = NULL;
  39.          cb.aio_sigevent.sigev_value.sival_ptr = &cb;
  40.         //发送异步读请求
  41.         ret = aio_read(&cb);
  42.         if (ret == -1)
  43.          {
  44.                   perror("aio_read");
  45.                   exit(1);
  46.         }
  47.         int i = 0;
  48.         while (1)
  49.          {
  50.                  printf("%d\n",i++);
  51.                  sleep(1);
  52.         }
  53.          return 0;
  54. }
复制代码
       程序开始声明了一个aiocb结构体,并初始化为0。以可读可写的方式打开一个名叫x4412的文件,以供后续读取操作。对aiocb结构体进行了一些必要的初始化,并设置完回调函数后,即调用aio_read函数发送异步读请求,然后进入死循环,等待回调函数读取文件。
       在回调函数async_read中,通过aio_error函数检查异步请求是否完成。当请求完成后,再调用aio_return函数获取返回值,它将返回从文件中读取的字符串的长度。读取的内容被记录在ptr->aio_buf中,可以通过printf函数将它的内容打印出来。
       使用如下指令编译应用程序:
arm-none-linux-gnueabi-gcc x4412-aio-app.c -ox4412-aio-app –lrt
       在测试应用程序时,首先在开发板的应用程序所在目录新建一个名为x4412的文件,并输入一些字符串,再执行应用程序观看效果。
  1. [root@x4412 mnt]# touch x4412
  2. [root@x4412 mnt]# echo 'i love x4412 board,i love ibox too.' > x4412
  3. [root@x4412 mnt]# ./x4412-aio-app
  4. 0
  5. ret = 36
  6. read=i love x4412 board,i love ibox too.

  7. 1
  8. 2
  9. 3
  10. 4

  11. [root@x4412 mnt]#
复制代码
编写应用程序,使用信号的方式进行异步通知,读取当前目录下的某文件,并将读取的内容打印出来。
       ubuntu用户目录或samba目录下新建应用程序x4412-aio-signal-app.c,编辑内容如下:
  1. #include <aio.h>
  2. #include <fcntl.h>
  3. #include <unistd.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <errno.h>
  7. #include <string.h>
  8. #define S_IRUSR 00400
  9. #define S_IWUSR 00200
  10. void x4412_aio_handler(int signo, siginfo_t *info, void *context)
  11. {
  12.          int ret;
  13.          struct aiocb *ptr = (struct aiocb *)info->si_value.sival_ptr;
  14.          if(aio_error(ptr)==0)//AIO请求完成?
  15.          {
  16.                    //请求完成,获得返回值
  17.                    ret = aio_return(ptr);
  18.                    printf("signo = %d,ret = %d\r\n",signo,ret);
  19.                    printf("read=%s\r\n", (char *)ptr->aio_buf);
  20.          }
  21. }
  22. int main(void)
  23. {
  24.          int fd;
  25.          struct sigaction sig_act;
  26.         struct aiocb cb;//API函数通过此结构检查
  27.         char sbuf[100];
  28.         int ret;
  29.          //以可读写的方式打开文件,同时应用对该文件也可读写
  30.         fd = open("x4412",O_RDWR,S_IRUSR|S_IWUSR);
  31.          if(fd==-1)
  32.          {
  33.                    printf("open file x4412 error!\r\n");
  34.                    return -1;
  35.          }
  36.          sigemptyset(&sig_act.sa_mask);
  37.          sig_act.sa_flags = SA_SIGINFO;
  38.          sig_act.sa_sigaction = x4412_aio_handler;
  39.         bzero(&cb, sizeof(cb));//清空aiocb结构
  40.         cb.aio_fildes = fd;//指向hello文件
  41.         cb.aio_buf = sbuf;
  42.         cb.aio_nbytes = 100;
  43.         cb.aio_offset = 0;//设置文件偏移量为0
  44.         /*设置回调函数*/
  45.         cb.aio_sigevent.sigev_notify = SIGEV_SIGNAL;//请求一个信号通知
  46.         cb.aio_sigevent.sigev_signo = SIGIO;//指定信号类型
  47.          cb.aio_sigevent.sigev_value.sival_ptr = &cb;
  48.          ret = sigaction(SIGIO, &sig_act,NULL);
  49.         //发送异步读请求
  50.         ret = aio_read(&cb);
  51.         if (ret == -1)
  52.          {
  53.                   perror("aio_read");
  54.                   exit(1);
  55.         }
  56.         int i = 0;
  57.         while (1)
  58.          {
  59.                  printf("%d\n",i++);
  60.                  sleep(1);
  61.         }
  62.          return 0;
  63. }
复制代码
       从源码可以看出,使用信号或使用回调函数通知的总体框架基本相同。使用如下指令编译应用程序:
  1. arm-none-linux-gnueabi-gcc x4412-aio-signal-app.c -o x4412-aio-signal-app –lrt
复制代码
       在测试应用程序时,首先在开发板的应用程序所在目录新建一个名为x4412的文件,并输入一些字符串,再执行应用程序观看效果。
  1. [root@x4412 mnt]# touch x4412
  2. [root@x4412 mnt]# echo 'i love x4412 board.' > x4412
  3. [root@x4412 mnt]# ./x4412-aio-signal-app
  4. 0
  5. signo = 29,ret = 20
  6. read=i love x4412 board.

  7. 1
  8. 2
  9. 3
  10. 4

  11. [root@x4412 mnt]#
复制代码
可在x4412开发板或ibox卡片电脑上运行的测试程序映像:
x4412-aio-app (6.77 KB, 下载次数: 7)
x4412-aio-signal-app (7.25 KB, 下载次数: 6)
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|深圳市九鼎创展科技官方论坛 ( 粤ICP备11028681号-2  

GMT+8, 2024-6-14 20:08 , Processed in 0.023219 second(s), 20 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表