异步io
异步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
文章最后发布于: 2018-06-11 00:05:33
相关阅读
根据市场来看,目前中小企业如果能把SEO优化做好,那么在网络上获取订单的机会是很大的。但是要如何通过网站优化推广产品,并获得订单
最近弄了一个放大镜的案例,所以在这里写了一下关于dom中offsetWidth等相关属性 给个图,大家体验下 有兴趣的可以自己去试下 <!DOC
今天在写文档时看见流程图中有PID算法,莫名熟悉,查了下是过程控制算法,对不起大学自动控制理论的老师-_-!PID算法在过程控制中,按偏差
平头哥SEO在浏览某网页的时候突然跳出提示 404 not found,这可着实惊呆了平头哥了,404 not found是什么意思呢?该怎么解决呢 ?404 not
1 history.go(0)2 location.reload()3 location=location4 location.assign(location)5 document.execCommand('Refresh')6