• 2. linux AIO 异步读写


    1.异步IO概念

    在传统的 I/O 模型中,有一个使用惟一句柄标识的 I/O 通道。在 UNIX 中,这些句柄是文件描述符(这对等同于文件、管道、套接字等等)。在阻塞 I/O 中,我们发起了一次传输操作,当传输操作完成或发生错误时,系统调用就会返回。

    在异步非阻塞 I/O 中,我们可以同时发起多个传输操作。这需要每个传输操作都有惟一的上下文,这样我们才能在它们完成时区分到底是哪个传输操作完成了。在 AIO 中,这是一个 aiocb(AIO I/O Control Block)结构。这个结构包含了有关传输的所有信息,包括为数据准备的用户缓冲区。在产生 I/O (称为完成)通知时,aiocb 结构就被用来惟一标识所完成的 I/O 操作。这个 API 的展示显示了如何使用它。

    2.异步IO基本API

    核心结构体

    struct aiocb
    {
        //要异步操作的文件描述符
        int aio_fildes;
        //用于lio操作时选择操作何种异步I/O类型
        int aio_lio_opcode;
        //异步读或写的缓冲区的缓冲区
        volatile void *aio_buf;
        //异步读或写的字节数
        size_t aio_nbytes;
        //异步通知的结构体
        struct sigevent aio_sigevent;
    }
    
    
    struct sigevent
    {
        sigval_t sigev_value;
        int sigev_signo;
        int sigev_notify;
        union {
            int _pad[SIGEV_PAD_SIZE];
             int _tid;
    
    
            struct {
                void (*_function)(sigval_t);
                void *_attribute;   /* really pthread_attr_t */
            } _sigev_thread;
        } _sigev_un;
    }
    
    
    #define sigev_notify_function   _sigev_un._sigev_thread._function
    #define sigev_notify_attributes _sigev_un._sigev_thread._attribute
    #define sigev_notify_thread_id   _sigev_un._tid
    
    
    API 说明
    aio_read 请求异步读操作
    aio_error 请求异步读操作
    aio_return 请求异步读操作
    aio_write 请求异步读操作
    aio_suspend 请求异步读操作
    aio_cancel 请求异步读操作
    aio_listio 请求异步读操作

    3. 异步读取aio_read

    /*
    该函数请求对文件进行异步读操作,若请求失败返回-1,成功则返回0,并将该请求进行排队,然后就开始对文件的异步读操作需要注意的是,我们得先对aiocb结构体进行必要的初始化
    */
    int aio_read(struct aiocb *paiocb);
    
    
    /*
    当其状态处于EINPROGRESS则I/O还没完成,当处于ECANCELLED则操作已被取消,发生错误返回-1
    */
    int aio_error(struct aiocb *aiopcb);
    
    
    //返回读写的字节数
    //如果操作没完成调用此函数,则会产生错误
    ssize_t aio_return(struct aiocb *paiocb);

    例程

    #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);
        }
    
    
        couter = 0;
    //  循环等待异步读操作结束
        while(aio_error(&rd) == EINPROGRESS)
        {
            printf("第%d次
    ",++couter);
        }
        //获取异步读返回值
        ret = aio_return(&rd);
    
    
        printf("
    
    返回值为:%d",ret);
    
    
    
        return 0;
    }

    注意:编译上述程序时必须在编译时再加一个-lrt

    4.异步写aio_write

    /*
    aio_write和aio_read函数类似,当该函数返回成功时,说明该写请求以进行排队(成功0,失败-1)
    其和aio_read调用时的区别是就是我们如果在打开文件是,flags设置了O_APPEND则我们在填充aiocb时不需要填充它的偏移量了
    */
    int aio_write(struct aiocb *paiocb);

    例程

    #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 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
    ");
        }
    
    
        //获得异步写的返回值
        ret = aio_return(&wr);
        printf("
    
    
    返回值为:%d
    ",ret);
    
    
        return 0;
    }

    5. aio_suspend & aio_cancel

    /*
    函数来挂起(或阻塞)调用进程,直到异步请求完成为止,此时会产生一个信号,或者发生其他超时操作。调用者提供了一个aiocb引用列表,其中任何一个完成都会导致 aio_suspend 返回。
    */
    int aio_suspend( const struct aiocb *const cblist[],
                      int n, const struct timespec *timeout );
    
    /*
    要取消对某个给定文件描述符的所有请求,我们需要提供这个文件的描述符,以及一个对 aiocbp 的 NULL 引用。如果所有的请求都取消了,这个函数就会返回 AIO_CANCELED;如果至少有一个请求没有被取消,那么这个函数就会返回 AIO_NOT_CANCELED;如果没有一个请求可以被取消,那么这个函数就会返回 AIO_ALLDONE。我们然后可以使用 aio_error 来验证每个 AIO 请求。如果这个请求已经被取消了,那么 aio_error 就会返回 -1,并且 errno 会被设置为 ECANCELED。
    */
    int aio_cancel( int fd, struct aiocb *aiocbp );
    
    

    6. lio_listio

    /*
    这个函数非常重要,因为这意味着我们可以在一个系统调用(一次内核上下文切换)中启动大量的 I/O 操作.ode 参数可以是 LIO_WAIT 或 LIO_NOWAIT。LIO_WAIT 会阻塞这个调用,直到所有的 I/O 都完成为止。在操作进行排队之后,LIO_NOWAIT 就会返回。list 是一个 aiocb 引用的列表,最大元素的个数是由 nent 定义的。注意 list 的元素可以为 NULL,lio_listio 会将其忽略。sigevent 引用定义了在所有 I/O 操作都完成时产生信号的方法。
    
    
    int lio_listio( int mode, struct aiocb *list[], int nent,
                       struct sigevent *sig );
    */

    例程

    #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("
    读返回值:%d",ret);
    
    
        ret = aio_return(&wr);
        printf("
    写返回值:%d",ret);
    
    
    
    
        return 0;
    }

    7. IO 完成时异步通知

    /*
    使用回调进行异步通知,该种通知方式使用一个系统回调函数来通知应用程序,要想完成此功能,我们必须在aiocb中设置我们想要进行异步回调的aiocb指针,以用来回调之后表示其自身
    */

    例程

    #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
    ");
    
    
        //判断请求是否成功
        if(aio_error(prd) == 0)
        {
            //获取返回值
            ret = aio_return(prd);
            printf("读返回值为:%d
    ",ret);
        }
    }
    
    
    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("异步读以开始
    ");
        sleep(1);
        printf("异步读结束
    ");
    
    
    
    
        return 0;
    }
  • 相关阅读:
    函数输出参数 双重指针
    NotePad++ 支持日语字体
    C++ 前置操作符与后置操作符
    用js判断 iPhone6 iPhone6 plus iphonex?
    从浏览器输入一个地址到渲染出网页这个过程发生了什么???
    对.Net 垃圾回收Finalize 和Dispose的理解
    在.NET环境中使用单元测试工具NUnit
    信道
    asp.net 获取当前URL的正确方法
    ASP.NET中常用输出JS脚本的类
  • 原文地址:https://www.cnblogs.com/standardzero/p/12552642.html
Copyright © 2020-2023  润新知