这里注意读写的buf都必须是按扇区对齐的,可以用posix_memalign来分配 。
3.获取完成的IO
long io_getevents (aio_context_t ctx_id, long min_nr, long nr, struct io_event *events, struct timespec *timeout);这里最重要的就是提供一个io_event数组给内核来copy完成的IO请求到这里,数组的大小是io_setup时指定的maxevents 。
timeout是指等待IO完成的超时时间,设置为NULL表示一直等待所有到IO的完成 。
4.销毁IO任务
int io_destroy (io_context_t ctx);注:需要C/C++ Linux服务器开发学习资料私信“资料”(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等),免费分享

文章插图
libaio和epoll的结合在异步编程中,任何一个环节的阻塞都会导致整个程序的阻塞,所以一定要避免在io_getevents调用时阻塞式的等待 。还记得io_iocb_common中的flags和resfd吗?看看libaio是如何提供io_getevents和事件循环的结合:
void io_set_eventfd(struct iocb *iocb, int eventfd){ iocb->u.c.flags |= (1 << 0) /* IOCB_FLAG_RESFD */; iocb->u.c.resfd = eventfd;}这里的resfd是通过系统调用eventfd生成的 。int eventfd(unsigned int initval, int flags);eventfd是linux 2.6.22内核之后加进来的syscall,作用是内核用来通知应用程序发生的事件的数量,从而使应用程序不用频繁地去轮询内核是否有时间发生,而是由内核将发生事件的数量写入到该fd,应用程序发现fd可读后,从fd读取该数值,并马上去内核读取 。有了eventfd,就可以很好地将libaio和epoll事件循环结合起来:
1. 创建一个eventfd
efd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);2. 将eventfd设置到iocb中io_set_eventfd(iocb, efd);3. 交接AIO请求io_submit(ctx, NUM_EVENTS, iocb);4. 创建一个epollfd,并将eventfd加到epoll中epfd = epoll_create(1);epoll_ctl(epfd, EPOLL_CTL_ADD, efd, &epevent);epoll_wait(epfd, &epevent, 1, -1);5. 当eventfd可读时,从eventfd读出完成IO请求的数量,并调用io_getevents获取这些IO【linux异步IO编程实例分析】
read(efd, &finished_aio, sizeof(finished_aio);r = io_getevents(ctx, 1, NUM_EVENTS, events, &tms);
文章插图
异步非阻塞IO模型的流程图
一个完整的编程实例
#define _GNU_SOURCE#define __STDC_FORMAT_macROS#include <stdio.h>#include <errno.h>#include <libaio.h>#include <sys/eventfd.h>#include <sys/epoll.h>#include <stdlib.h>#include <sys/types.h>#include <unistd.h>#include <stdint.h>#include <sys/stat.h>#include <fcntl.h>#include <inttypes.h>#define TEST_FILE "aio_test_file"#define TEST_FILE_SIZE (127 * 1024)#define NUM_EVENTS 128#define ALIGN_SIZE 512#define RD_WR_SIZE 1024struct custom_iocb{ struct iocb iocb; int nth_request;};void aio_callback(io_context_t ctx, struct iocb *iocb, long res, long res2){ struct custom_iocb *iocbp = (struct custom_iocb *)iocb; printf("nth_request: %d, request_type: %s, offset: %lld, length: %lu, res: %ld, res2: %ldn", iocbp->nth_request, (iocb->aio_lio_opcode == IO_CMD_PREAD) ? "READ" : "WRITE", iocb->u.c.offset, iocb->u.c.nbytes, res, res2);}int main(int argc, char *argv[]){ int efd, fd, epfd; io_context_t ctx; struct timespec tms; struct io_event events[NUM_EVENTS]; struct custom_iocb iocbs[NUM_EVENTS]; struct iocb *iocbps[NUM_EVENTS]; struct custom_iocb *iocbp; int i, j, r; void *buf; struct epoll_event epevent; efd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); if (efd == -1) { perror("eventfd"); return 2; } fd = open(TEST_FILE, O_RDWR | O_CREAT | O_DIRECT, 0644); if (fd == -1) { perror("open"); return 3; } ftruncate(fd, TEST_FILE_SIZE); ctx = 0; if (io_setup(8192, &ctx)) { perror("io_setup"); return 4; } if (posix_memalign(&buf, ALIGN_SIZE, RD_WR_SIZE)) { perror("posix_memalign"); return 5; } printf("buf: %pn", buf); for (i = 0, iocbp = iocbs; i < NUM_EVENTS; ++i, ++iocbp) { iocbps[i] = &iocbp->iocb; io_prep_pread(&iocbp->iocb, fd, buf, RD_WR_SIZE, i * RD_WR_SIZE); io_set_eventfd(&iocbp->iocb, efd); io_set_callback(&iocbp->iocb, aio_callback); iocbp->nth_request = i + 1; } if (io_submit(ctx, NUM_EVENTS, iocbps) != NUM_EVENTS) { perror("io_submit"); return 6; } epfd = epoll_create(1); if (epfd == -1) { perror("epoll_create"); return 7; } epevent.events = EPOLLIN | EPOLLET; epevent.data.ptr = NULL; if (epoll_ctl(epfd, EPOLL_CTL_ADD, efd, &epevent)) { perror("epoll_ctl"); return 8; } i = 0; while (i < NUM_EVENTS) { uint64_t finished_aio; if (epoll_wait(epfd, &epevent, 1, -1) != 1) { perror("epoll_wait"); return 9; } if (read(efd, &finished_aio, sizeof(finished_aio)) != sizeof(finished_aio)) { perror("read"); return 10; } printf("finished io number: %"PRIu64"n", finished_aio); while (finished_aio > 0) { tms.tv_sec = 0; tms.tv_nsec = 0; r = io_getevents(ctx, 1, NUM_EVENTS, events, &tms); if (r > 0) { for (j = 0; j < r; ++j) { ((io_callback_t)(events[j].data))(ctx, events[j].obj, events[j].res, events[j].res2); } i += r; finished_aio -= r; } } } close(epfd); free(buf); io_destroy(ctx); close(fd); close(efd); remove(TEST_FILE); return 0;}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Linux和Windows两种风格的操作系统,创建线程的方式有何不同?
- 计算机编程必备技巧——使用递归
- Linux Scsi子系统框架介绍
- Java8——异步编程
- 新手怎么才能学好UG/CNC编程呢?首先得先了解三大加工流程
- linux之间传文件命令之Rsync傻瓜式教程
- 微软提交补丁,用Linux替代Hyper-V根分区的Win
- 借助 HTTP 通过 SSH 绕过 Linux 防火墙
- Linux或者Mac下命令行speedtest测试网络速度
- 在浏览器中异步下载文件监听下载进度
