异步I/O(一)

异步I/O(一)

概述


    AIO可以一次性发出大量的read/write调用并且通过通用块层的IO调度来获得更好的性能,用户程序也可以减少过多的同步负载,还可以在业务逻辑中更灵活的进行并发控制和负载均衡。

    POSIX异步I/O(AIO)接口允许应用程序启动一个或多个异步执行的I/O操作。应用程序可以选择以各种方式通知I/O操作的完成:通过传递信号,通过实例化线程或运行完后没有通知。

    控制块缓冲区和aio_buf指向的缓冲区在I / O操作正在进行时不能改变。 这些缓冲区必须保持有效,直到I / O操作完成。

    使用相同的aiocb结构同时进行异步读取或写入操作会产生未定义的结果。

    当前的Linux POSIX AIO实现由glibc在用户空间中提供。 这具有许多局限性,最值得注意的是维护多个线程来执行I / O操作是昂贵的,并且规模很小。 基于状态机的异步I / O实现(参见io_submit(2),io_setup(2),io_cancel(2),io_destroy(2),io_getevents(2))已经有一段时间了, 但是这种实现还没有成熟到POSIX AIO实现可以使用内核系统调用完全重新实现的程度。

异步I/O相关接口

POSIXAIO接口由以下功能组成

        aio_read排队读取请求。这是read函数的异步模拟。

        aio_write排队写入请求。这是write函数的异步模拟。

        aio_fsync为文件描述符上的I/O操作排队同步请求。这是fsync和fdatasync的异步模拟。

        aio_error获取入队I/O请求的错误状态。

        aio_return获取完成的I/O请求的返回状态。

        aio_suspend挂起调用者,直到一个或多个指定的I/O请求完成。

        aio_cancel尝试取消指定文件描述符上的未完成I/O请求。

         lio_listio使用单个函数调用排队多个I/O请求。



重要的数据结构

        aiocb(“异步I/O控制块”)结构定义了控制I/O操作的参数。这种类型的论点被用于上面列出的所有功能。该结构主要的成员如下:


        #include<aiocb.h>

        structaiocb{

        


                intaio_fildes;/*文件描述符*/

                off_taio_offset;/*文件偏移*/

                volatilevoid*aio_buf;/*缓冲区的位置*/

                size_taio_nbytes;/*传输长度*/

                intaio_reqprio;/*请求优先权*/

                structsigeventaio_sigevent;/*通知方式*/

                intaio_lio_opcode;/*要执行的操作;lio_listio()only*/


        };

        /*'aio_lio_opcode'的操作码:*/


        enum{LIO_READ,LIO_WRITE,LIO_NOP};



测试例子及说明

文件拷贝-阻塞+缓存版本

#include <ctype.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>

#define BSZ 4096

unsigned char buf[BSZ];

#define FILE_MODE       (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)

int main(int argc, char* argv[])
{
        int     ifd, ofd, i, n, nw;

        if (argc != 3){
                printf("usage: cp infile outfile");
                exit(-1);

        }

        if ((ifd = open(argv[1], O_RDONLY)) < 0){
                printf("can't open %s", argv[1]);
                exit(-1);

        }

        if ((ofd = open(argv[2], O_RDWR|O_CREAT|O_TRUNC, FILE_MODE)) < 0){
                printf("can't create %s", argv[2]);
                exit(-1);

        }

        while ((n = read(ifd, buf, BSZ)) > 0) {
                if ((nw = write(ofd, buf, n)) != n) {
                       if (nw < 0){
                                printf("write failed"); 
                                exit(-1);

                        }else{
                                printf("short write (%d/%d)", nw, n);
                                exit(-1);
                        }
                }
        }


        fsync(ofd);
        exit(0);

}


异步IO拷贝文件版本

#include <ctype.h>
#include <fcntl.h>
#include <aio.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#define BSZ 4096
#define NBUF 4
#define FILE_MODE       (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)

enum rwop {
        UNUSED = 0,
        READ_PENDING = 1,
        WRITE_PENDING = 2
};

struct buf {
        enum rwop     op;
        int           last;
        struct aiocb  aiocb;
        unsigned char data[BSZ];
};

struct buf bufs[NBUF];
int main(int argc, char* argv[])
{

        int                                     ifd, ofd, i,  n, err, numop;
        struct stat                     sbuf;
        const struct aiocb      *aiolist[NBUF];
        off_t                           off = 0;

        if (argc != 3){
                printf("usage: cp infile outfile");
                exit(-1);
        }

        if ((ifd = open(argv[1], O_RDONLY)) < 0){
                printf("can't open %s", argv[1]);
                exit(-1);

        }

        if ((ofd = open(argv[2], O_RDWR|O_CREAT|O_TRUNC, FILE_MODE)) < 0){
                printf("can't create %s", argv[2]);
                exit(-1);
        }

        if (fstat(ifd, &sbuf) < 0){
                printf("fstat failed");
                exit(-1);
        }



        /* initialize the buffers */
        for (i = 0; i < NBUF; i++) {
                bufs[i].op = UNUSED;
                bufs[i].aiocb.aio_buf = bufs[i].data;
                bufs[i].aiocb.aio_sigevent.sigev_notify = SIGEV_NONE;
                aiolist[i] = NULL;
        }



        numop = 0;
        for (;;) {
                for (i = 0; i < NBUF; i++) {
                        switch (bufs[i].op) {
                        case UNUSED:
                                /*
                                 * Read from the input file if more data
                                 */

                                if (off < sbuf.st_size) {
                                        bufs[i].op = READ_PENDING;
                                        bufs[i].aiocb.aio_fildes = ifd;
                                        bufs[i].aiocb.aio_offset = off;
                                        off += BSZ;
                                        if (off >= sbuf.st_size)
                                                bufs[i].last = 1;
                                        bufs[i].aiocb.aio_nbytes = BSZ;
                                        if (aio_read(&bufs[i].aiocb) < 0){
                                                printf("aio_read failed");
                                                exit(-1);
                                        }
                                        aiolist[i] = &bufs[i].aiocb;

                                        numop++;

                                }

                                break;

                        case READ_PENDING:

                                if ((err = aio_error(&bufs[i].aiocb)) == EINPROGRESS)

                                        continue;

                                if (err != 0) {

                                        if (err == -1){
                                                printf("aio_error failed");
                                                exit(-1);

                                        }else{
                                                printf("%s\n", "read failed");
                                                exit(-1);
                                        }
                                }

                                /*
                                 * A read is complete and write it.
                                 */

                                if ((n = aio_return(&bufs[i].aiocb)) < 0)
                                        printf("aio_return failed");

                                if (n != BSZ && !bufs[i].last)
                                        printf("short read (%d/%d)", n, BSZ);
                                bufs[i].op = WRITE_PENDING;
                                bufs[i].aiocb.aio_fildes = ofd;
                                bufs[i].aiocb.aio_nbytes = n;
                                if (aio_write(&bufs[i].aiocb) < 0){
                                        printf("aio_write failed");     
                                        exit(-1);
                                }

                                break;

                        case WRITE_PENDING:
                                if ((err = aio_error(&bufs[i].aiocb)) == EINPROGRESS)
                                        continue;
                                if (err != 0) {
                                        if (err == -1){
                                                printf("aio_error failed");
                                                exit(-1);
                                        }else{
                                                printf("%s", "write failed");
                                                exit(-1);
                                        }
                                }
                                /*
                                 * A write is complete; 
                                 */

                                if ((n = aio_return(&bufs[i].aiocb)) < 0)
                                        printf("aio_return failed");
                                if (n != bufs[i].aiocb.aio_nbytes)
                                        printf("short write (%d/%d)", n, BSZ);

                                aiolist[i] = NULL;
                                bufs[i].op = UNUSED;
                                numop--;
                                break;

                        }
                }

                if (numop == 0) {
                        if (off >= sbuf.st_size)
                                break;
                } else {
                        if (aio_suspend(aiolist, NBUF, NULL) < 0){
                                printf("aio_suspend failed");
                                exit(-1);
                        }
                }
        }



        bufs[0].aiocb.aio_fildes = ofd;
        if (aio_fsync(O_SYNC, &bufs[0].aiocb) < 0){
                printf("aio_fsync failed");
                exit(-1);
        }

        exit(0);

}



运行说明

[root@bogon root]# time ./cp2  /etc/services 1

real    0m0.033s

user    0m0.001s

sys     0m0.007s


 [root@bogon root]# time ./cp1 /etc/services 1

real    0m0.017s

user    0m0.000s

sys     0m0.004s


从上面的运行结果可以看出异步IO运行时间比阻塞+缓存版本的事件长。原因是异步IO需要更多的同步时间。可以运行strace命令查看。具体情况如下所示



阻塞+缓存版本strace如下:

strace -c ./cp  /etc/services 1

% time     seconds  usecs/call     calls    errors syscall

------ ----------- ----------- --------- --------- ----------------

 33.92    0.001592          10       164           write

 32.59    0.001530           9       168           read

 19.47    0.000914         152         6           open

 10.18    0.000478         478         1           fsync

  1.51    0.000071           9         8           mprotect

  1.07    0.000050           4        13           mmap

  0.51    0.000024          24         1           munmap

  0.17    0.000008           4         2           rt_sigaction

  0.11    0.000005           1         4           fstat

  0.11    0.000005           5         1           arch_prctl

  0.09    0.000004           1         4           close

  0.09    0.000004           4         1           getrlimit

  0.06    0.000003           3         1           rt_sigprocmask

  0.06    0.000003           3         1           set_tid_address

  0.06    0.000003           3         1           set_robust_list

  0.00    0.000000           0         1           brk

  0.00    0.000000           0         1         1 access

  0.00    0.000000           0         1           execve

------ ----------- ----------- --------- --------- ----------------

100.00    0.004694                   379         1 total


异步IO版本strace如下:

[root@bogon advio]# strace -c ./cp /etc/services 1

% time     seconds  usecs/call     calls    errors syscall

------ ----------- ----------- --------- --------- ----------------

 86.53    0.005877           9       685         1 futex

  6.63    0.000450          75         6           open

  2.03    0.000138           9        15           mmap

  1.59    0.000108          11        10           mprotect

  0.66    0.000045          23         2           clone

  0.34    0.000023          23         1           munmap

  0.32    0.000022           4         5           fstat

  0.32    0.000022           4         5           rt_sigprocmask

  0.31    0.000021           5         4           brk

  0.25    0.000017           6         3           read

  0.24    0.000016           4         4           close

  0.19    0.000013          13         1         1 access

  0.13    0.000009           5         2           rt_sigaction

  0.07    0.000005           5         1           execve

  0.06    0.000004           4         1           getrlimit

  0.06    0.000004           4         1           sched_getparam

  0.06    0.000004           4         1           sched_getscheduler

  0.06    0.000004           4         1           arch_prctl

  0.06    0.000004           4         1           set_tid_address

  0.04    0.000003           3         1           fcntl

  0.04    0.000003           3         1           set_robust_list

------ ----------- ----------- --------- --------- ----------------

100.00    0.006792                   751         2 total


从上可以看出来异步IO很多的时间都花在了futex同步上了。



参考资料

[1].http://blog.sina.com.cn/s/blog_6f55491501016diq.html

[2]. http://www.wzxue.com/linux-kernel-aio这个奇葩

[3]. http://man7.org/linux/man-pages/man7/aio.7.html 



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值