美文网首页
2019-04-5 Linux中的AIO

2019-04-5 Linux中的AIO

作者: 知成 | 来源:发表于2019-06-16 18:25 被阅读0次

Linux中的AIO


产生AIO的原因:

计算机CPU的执行速度远大于I/O读写的执行速度,如果我们用传统的阻塞式或非阻塞式来操作I/O的话,那么我们在同一个程序中(不用多线程或多进程)就不能同时操作俩个以上的文件I/O,每次只能对一个文件进行I/O操作,很明显这样效率很低下(因为CPU速度远大于I/O操作的速度,所以当执行I/O时,CPU其实还可以做更多的事)。因此就诞生了相对高效的异步I/O

异步aio的基本API

API函数 说明
aio_read 异步读操作
aio_write 异步写操作
aio_error 检查异步请求的状态
aio_return 获得异步请求完成时的返回值
aio_suspend 挂起调用进程,直到一个或多个异步请求已完成
aio_cancel 取消异步请求
lio_list 发起一系列异步I/O请求

AIO结构体aiocb

struct aiocb
{ //要异步操作的文件描述符 
    int aio_fildes; 
    //用于aio操作时选择操作何种异步I/O类型 
    int aio_lio_opcode; 
    //异步读或写的缓冲区的缓冲区 
    volatile void *aio_buf; 
    //异步读或写的字节数 
    size_t aio_nbytes; 
    //异步通知的结构体 
    struct sigevent aio_sigevent; 
    off_t aio_offset;     
    /*1,size_t:跟机器字长一样;
    2,off_t:32位机器下默认是32位长,这时无法对大于4G的文件偏移操作,这时off_t = __off_t;如果想进行大于4G的文件偏移操作,可以在程序中加入头文件之前定义这时off_t = __off64_t,具体定义在unistd.h中;对于64位机,默认就是64位长。
    
#define _FILE_OFFSET_BITS 64
3,ino_t:跟机器字长一样。*/
    int aio_reqprio;    //请求优先级

};

异步I/O操作的具体使用

aio_read

  • aio_read 函数请求对一个有效的文件描述符进行异步读操作。这个文件描述符可以表示一个文件、套接字甚至管道。aio_read 函数的原型如下
int aio_read(struct aiocb *aiocbp);
  • aio_read 函数在请求进行排队之后会立即返回。如果执行成功,返回值就为 0;如果出现错误,返回值就为 -1,并设置 errno 的值。

aio_error

  • aio_error 函数被用来确定请求的状态。
int aio_error( struct aiocb *aiocbp );
  • 返回值

EINPROGRESS,说明请求尚未完成
ECANCELLED,说明请求被应用程序取消了
-1,说明发生了错误,具体错误原因可以查阅 errno
0 ,说明完成当前请求

o_return

  • 异步 I/O 和标准块 I/O 之间的另外一个区别是我们不能立即访问这个函数的返回状态,因为我们并没有阻塞在 read 调用上。在标准的 read 调用中,返回状态是在该函数返回时提供的。但是在异步 I/O 中,我们要使用 aio_return 函数。这个函数的原型如下:
ssize_t aio_return( struct aiocb *aiocbp );
  • 返回值为读入或写入的长度

  • demo

    #include<stdio.h>
    #include<sys/socket.h>
    #include<netinet/in.h>
    #include<arpa/inet.h>
    #include<assert.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<errno.h>
    #include<string.h>
    #include<sys/types.h>
    #include<fcntl.h>
    #include<aio.h>
    
    
    #define BUFFER_SIZE 1024
    
    int MAX_LIST = 2;
    
    int main(int argc,char **argv)
    {
        //aio操作所需结构体
        struct aiocb rd;
    
        int fd,ret,couter;
    
        fd = open("test.txt",O_RDONLY);
        if(fd < 0)
        {
            perror("test.txt");
        }
    
    
    
        //将rd结构体清空
        bzero(&rd,sizeof(rd));
    
    
        //为rd.aio_buf分配空间
        rd.aio_buf = malloc(BUFFER_SIZE + 1);
    
        //填充rd结构体
        rd.aio_fildes = fd;
        rd.aio_nbytes = BUFFER_SIZE;
        rd.aio_offset = 0;
    
        //进行异步读操作
        ret = aio_read(&rd);
        if(ret < 0)
        {
            perror("aio_read");
            exit(1);
        }
      //do other things
      
        couter = 0;
    //  循环等待异步读操作结束
        while(aio_error(&rd) == EINPROGRESS)
        {
           // printf("第%d次\n",++couter);
        }
      
        //获取异步读返回值
        ret = aio_return(&rd);
      
        printf("\n\n返回值为:%d\n",ret);
      printf("%s\n",rd.aio_buf);
      
      free(rd.aio_buf);
      close(fd);
        return 0;
    }
    

    aio_write

    • aio_write 函数用来请求一个异步写操作。其函数原型如下:
    int aio_write( struct aiocb *aiocbp );
    
  • aio_write 函数会立即返回,说明请求已经进行排队(成功时返回值为 0,失败时返回值为 -1,并相应地设置 errno)。

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

demo

#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<assert.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<fcntl.h>
#include<aio.h>

#define BUFFER_SIZE 1024

int main(int argc,char **argv)
{
    //定义aio控制块结构体
    struct aiocb wr;

    int ret,fd;

    char str[20] = {"hello,world"};

    //置零wr结构体
    bzero(&wr,sizeof(wr));

    fd = open("test.txt",O_WRONLY | O_APPEND);
    if(fd < 0)
    {
        perror("test.txt");
    }

    //为aio.buf申请空间
    wr.aio_buf = (char *)malloc(BUFFER_SIZE);
    if(wr.aio_buf == NULL)
    {
        perror("buf");
    }

    wr.aio_buf = str;

    //填充aiocb结构
    wr.aio_fildes = fd;
    wr.aio_nbytes = 1024;

    //异步写操作
    ret = aio_write(&wr);
    if(ret < 0)
    {
        perror("aio_write");
    }

    //等待异步写完成
    while(aio_error(&wr) == EINPROGRESS)
    {
        printf("hello,world\n");
    }

    //获得异步写的返回值
    ret = aio_return(&wr);
    printf("\n\n\n返回值为:%d\n",ret);

    return 0;
}

aio_suspend

aio_suspend函数可以使当前进程挂起,直到有向其注册的异步事件完成为止

  • 阻塞

  • 当有AIO请求返程后,该函数返回

int aio_suspend(const struct aiocb *const cblist[],int n,const struct timespec *timeout);
  • 第一个参数是个保存了aiocb块地址的数组,我们可以向其内添加想要等待阻塞的异步事件,第二个参数为向cblist注册的aiocb个数,第三个参数为等待阻塞的超时时间,NULL为无限等待

demo

#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<assert.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<fcntl.h>
#include<aio.h>


#define BUFFER_SIZE 1024

int MAX_LIST = 2;

int main(int argc,char **argv)
{
    //aio操作所需结构体
    struct aiocb rd;
    struct aiocb wd;
    int fd,ret,couter;

    //cblist链表
    struct aiocb *aiocb_list[2];



    fd = open("test.txt",O_RDONLY);
    if(fd < 0)
    {
        perror("test.txt");
    }



    //将rd结构体清空
    bzero(&rd,sizeof(rd));


    //为rd.aio_buf分配空间
    rd.aio_buf = malloc(BUFFER_SIZE + 1);

    //填充rd结构体
    rd.aio_fildes = fd;
    rd.aio_nbytes =  BUFFER_SIZE;
    rd.aio_offset = 0;

    //将读fd的事件注册
    aiocb_list[0] = &rd;

    //进行异步读操作
    ret = aio_read(&rd);
    if(ret < 0)
    {
        perror("aio_read");
        exit(1);
    }

    printf("我要开始等待异步读事件完成\n");
    //阻塞等待异步读事件完成
    ret = aio_suspend(aiocb_list,MAX_LIST,NULL);
    
    
    //获取异步读返回值
    ret = aio_return(&rd);

    printf("\n\n返回值为:%d\n",ret);


    return 0;
}

lio_listio

aio同时还为我们提供了一个可以发起多个或多种I/O请求的接口lio_listio
这个函数效率很高,因为我们只需一次系统调用(一次内核上下位切换)就可以完成大量的I/O操作

int lio_listio(int mode,struct aiocb *list[],int nent,struct sigevent *sig);

第一个参数mode可以有俩个实参,LIO_WAIT和LIO_NOWAIT,前一个会阻塞该调用直到所有I/O都完成为止,后一个则会挂入队列就返回

  • LIO_WAIT 阻塞发起
  • LIO_NOWAIT 非阻塞发起

批量发起AIO的两种方法

  • 阻塞等到所有发起的AIO全部完成后,才会返回
  • 发起后立即返回,通过绑定的信号来通知

LIO_WAIT demo

#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<assert.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<fcntl.h>
#include<aio.h>

#define BUFFER_SIZE 1025

int MAX_LIST = 2;


int main(int argc,char **argv)
{
    struct aiocb *listio[2];
    struct aiocb rd,wr;
    int fd,ret;

    //异步读事件
    fd = open("test1.txt",O_RDONLY);
    if(fd < 0)
    {
        perror("test1.txt");
    }

    bzero(&rd,sizeof(rd));

    rd.aio_buf = (char *)malloc(BUFFER_SIZE);
    if(rd.aio_buf == NULL)
    {
        perror("aio_buf");
    }

    rd.aio_fildes = fd;
    rd.aio_nbytes = 1024;
    rd.aio_offset = 0;
    rd.aio_lio_opcode = LIO_READ;   ///lio操作类型为异步读

    //将异步读事件添加到list中
    listio[0] = &rd;


    //异步些事件
    fd = open("test2.txt",O_WRONLY | O_APPEND);
    if(fd < 0)
    {
        perror("test2.txt");
    }

    bzero(&wr,sizeof(wr));

    wr.aio_buf = (char *)malloc(BUFFER_SIZE);
    if(wr.aio_buf == NULL)
    {
        perror("aio_buf");
    }

    wr.aio_fildes = fd;
    wr.aio_nbytes = 1024;

    wr.aio_lio_opcode = LIO_WRITE;   ///lio操作类型为异步写

    //将异步写事件添加到list中
    listio[1] = &wr;

    //使用lio_listio发起一系列请求
    ret = lio_listio(LIO_WAIT,listio,MAX_LIST,NULL);

    //当异步读写都完成时获取他们的返回值

    ret = aio_return(&rd);
    printf("\n读返回值:%d",ret);

    ret = aio_return(&wr);
    printf("\n写返回值:%d",ret);



    return 0;
}


LIO_NOWAIT demo

当我们的异步I/O操作完成之时,我们可以通过信号通知我们的进程也可用回调函数来进行异步通知,接下来我会为大家主要介绍以下回调函数来进行异步通知

struct sigevent {
    int           sigev_notify;            //Notification type. 
    int           sigev_signo;            //Signal number. 
    union sigval  sigev_value;             //Signal value. 
    void         (*sigev_notify_function)(union sigval); //Notification function. 
    pthread_attr_t *sigev_notify_attributes;  //Notification attributes. 
}; 
union sigval
{
    int sival_int;
    void *sival_ptr;
};  

demo

#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<assert.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<fcntl.h>
#include<aio.h>
#include<unistd.h>

#define BUFFER_SIZE 1025


void aio_completion_handler(sigval_t sigval)
{
    //用来获取读aiocb结构的指针
    struct aiocb *prd;
    int ret;

    prd = (struct aiocb *)sigval.sival_ptr;

    printf("hello\n");

    //获取返回值
    ret = aio_return(prd);

}

int main(int argc,char **argv)
{
    int fd,ret;
    struct aiocb rd;

    fd = open("test.txt",O_RDONLY);
    if(fd < 0)
    {
        perror("test.txt");
    }



    //填充aiocb的基本内容
    bzero(&rd,sizeof(rd));

    rd.aio_fildes = fd;
    rd.aio_buf = (char *)malloc(sizeof(BUFFER_SIZE + 1));
    rd.aio_nbytes = BUFFER_SIZE;
    rd.aio_offset = 0;

    //填充aiocb中有关回调通知的结构体sigevent
    rd.aio_sigevent.sigev_notify = SIGEV_THREAD;//使用线程回调通知
    rd.aio_sigevent.sigev_notify_function = aio_completion_handler;//设置回调函数
    rd.aio_sigevent.sigev_notify_attributes = NULL;//使用默认属性
    rd.aio_sigevent.sigev_value.sival_ptr = &rd;//在aiocb控制块中加入自己的引用

    //异步读取文件
    ret = aio_read(&rd);
    if(ret < 0)
    {
        perror("aio_read");
    }

    printf("异步读以开始\n");
    sleep(1);
    printf("异步读结束\n");



    return 0;
}

相关文章

  • 2019-04-5 Linux中的AIO

    Linux中的AIO 产生AIO的原因: 计算机CPU的执行速度远大于I/O读写的执行速度,如果我们用传统的阻塞式...

  • 使用异步 I/O 提高程序性能

    AIO 简介 Linux 异步(asynchronous) I/O 是 Linux 2.6 内核的标准功能,你也可...

  • 绿色部署总结

    mysql 绿色版安装win10 linux解决InnoDB: Linux Native AIO interfac...

  • BIO,NIO,AIO 总结

    BIO,NIO,AIO 总结 Java 中的 BIO、NIO和 AIO 理解为是 Java 语言对操作系统的各种 ...

  • BIO,NIO,AIO 总结

    BIO,NIO,AIO 总结 Java 中的 BIO、NIO和 AIO 理解为是 Java 语言对操作系统的各种 ...

  • Netty和NIO-未完待续

    linux底层支持bio(阻塞io),nio(多路复用io),aio(异步非阻塞io),信号io等多种方案; jd...

  • BIO/NIO/AIO的几个思考

    BIO/NIO/AIO中的IO指的是什么?为什么网上一讲到BIO/NIO/AIO就是Socket编程、网络编程bl...

  • 即时通讯框架——技术选型参考

    1、即时通讯框架talent-aio http://www.talent-aio.com/talent-aio/

  • Java AIO基础API

    JDK1.7 AIO是异步非阻塞的,代码上AIO简化了NIO很多繁琐的实现。 AIOServer.java AIO...

  • DirectIO的对齐问题

    最近在代码中使用了Linux AIO接口,其需要通过O_DIRECT方式打开文件,同时在IO时要求块大小对齐。 对...

网友评论

      本文标题:2019-04-5 Linux中的AIO

      本文链接:https://www.haomeiwen.com/subject/nsggfctx.html