`
yuexiaodong
  • 浏览: 70366 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

linux AIO libaio和epoll实现非阻塞模型

阅读更多

      epoll是Linux内核为处理大批句柄而作改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著的减少程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。因为它会复用文件描述符集合来传递结果而不是迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合,另一个原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。epoll除了提供select\poll那种IO事件的电平触发(Level Triggered)外,还提供了边沿触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提供应用程序的效率。

     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
read(efd, &finished_aio, sizeof(finished_aio);
r = io_getevents(ctx, 1, NUM_EVENTS, events, &tms);

 epoll 编程实例,测试通过:

#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  1024

struct 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: %ld\n", 
            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: %p\n", 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;
}

 说明:

1. 在centos 6.2 (libaio-devel 0.3.107-10) 上运行通过

2. struct io_event中的res字段表示读到的字节数或者一个负数错误码。在后一种情况下,-res表示对应的

   errno。res2字段为0表示成功,否则失败

3. iocb在aio请求执行过程中必须是valid的

4. 在上面的程序中,通过扩展iocb结构来保存额外的信息(nth_request),并使用iocb.data

   来保存回调函数的地址。如果回调函数是固定的,那么也可以使用iocb.data来保存额外信息。

 

原文 :http://blog.sina.com.cn/s/blog_6b19f21d0100znza.html

分享到:
评论

相关推荐

    linux_aio.zip_LINUX下开启AIO_aio glibc_aio 两种方式_linux aio_linux_aio

    本压缩包"linux_aio.zip"包含了关于如何在Linux下开启AIO以及使用glibc的AIO函数的实践代码和相关文档。下面将详细介绍这两种方式。 首先,我们来理解一下传统的同步I/O模型。在这种模型中,当应用程序发起一个I/O...

    i.MX6ULL实现非阻塞IO访问【Linux驱动】.zip

    通过理解和掌握上述知识点,开发者可以编写出高效、可靠的i.MX6ULL Linux驱动程序,实现对硬件的非阻塞I/O访问,充分利用硬件资源,提高系统的并发性和响应速度。项目的代码可以直接编译运行,为学习和实践提供了...

    linux下非阻塞IO程序源码.zip

    在Linux操作系统中,非阻塞I/O(Non-blocking I/O)是一种重要的编程模型,它用于处理高并发和低延迟的场景。与传统的阻塞I/O不同,非阻塞I/O在进行读写操作时,如果数据尚未准备好,不会挂起进程,而是立即返回一个...

    网络IO模型 Linux环境下的network IO 高清 目录 书签

    在Linux环境下,网络I/O(Input/Output)模型是系统进行网络通信的重要组成部分,它涉及到如何高效地处理来自网络的数据传输。本资源主要探讨的是在...通过深入学习这些知识,可以更好地设计和实现高性能的网络服务。

    5种技术处理高TCP并发.zip

    本文将深入探讨标题"5种技术处理高TCP并发"所涵盖的五种策略,即select、epoll、iocp、aio以及poll,并在Linux环境下基于C语言的实现。 1. **select**: select是最早用于多路复用I/O的机制之一。它允许程序监视多...

    Linux_C_io.rar_linux omap

    Linux支持五种基本的I/O模型:同步阻塞I/O、同步非阻塞I/O、I/O多路复用(如select、poll、epoll)、信号驱动I/O和异步I/O(AIO)。在C语言中,我们通常使用标准库函数如read、write、open、close等进行基本的文件...

    LINUX编程白皮书001-08

    6. **异步I/O**:介绍AIO(异步输入输出)模型,以及Linux中的libaio库,用于在后台处理I/O操作,让程序能够继续执行其他任务。 7. **错误处理与调试**:讲解如何处理网络编程中常见的错误,如连接失败、超时、...

    ACE前摄器TProactor

    在Linux中,AIO接口通常被称为libaio,而在Windows中,可以通过BeginRead/BeginWrite等API实现异步I/O操作。虽然AIO模型与TProactor的工作方式有所不同,但两者都旨在提高I/O密集型应用的性能。 在ACE中,TProactor...

    《linux 网络编程》 ppt 示例代码

    在Linux中,程序员可以使用socket API来创建和管理TCP套接字,实现可靠的双向数据流通信。 - 套接字创建:`socket()`函数用于创建一个套接字描述符。 - 绑定:`bind()`函数将套接字与特定的IP地址和端口号关联。 ...

    一个简单的异步IO库

    2. **AIO(Asynchronous I/O)**:使用`libaio`库,通过异步I/O操作接口(如`lio_listio()`)发起I/O请求,然后在完成后通过信号或者回调函数得到通知。 3. **SIGIO**:利用信号机制,当文件描述符上的I/O事件发生...

    linux文件操作的源码总结与示例

    在Linux操作系统中,文件操作是系统编程的核心部分。本文将深入探讨Linux文件操作的源码,提供相关的示例,帮助开发者理解并熟练掌握这一领域的知识。 1. **文件描述符(File Descriptors)** 文件描述符是Linux内核...

    详细介绍IO管理器的一篇报告

    2. **Linux**:Linux的IO管理器利用内核级别的AIO(异步IO)接口,以及用户空间的libaio库,支持异步IO。同时,Linux的epoll接口提供了高效多路复用IO的能力。 3. **Unix**:Unix系统通常使用select、poll和kqueue...

    unix环境高级编程&&unix网络编程

    6. 异步I/O:介绍异步I/O模型,如 aio 和 libaio 库的使用。 7. 网络安全:涉及网络编程中的安全问题,如认证、加密和防止拒绝服务攻击。 8. 进程间网络通信:讨论进程间的网络通信,如使用套接字进行跨主机的进程...

    UNIX 网络编程 卷一 (中文第二版)

    - **select()、poll()和epoll()**:这三种机制用于监控多个套接字的状态,实现非阻塞I/O和事件驱动编程。 6. **异步I/O**: - **SIGIO信号**:利用SIGIO信号处理异步套接字事件。 - ** aio库**:介绍UNIX提供的...

Global site tag (gtag.js) - Google Analytics