epoll详解(一)-- epoll在异步网络编程中的应用

在epoll模型的讲解中,这篇文章写的非常简单易懂,因此翻译过来分享给大家,做为epoll的入门。
原文地址:http://kovyrin.net/2006/04/13/epoll-asynchronous-network-programming/

一般情况下,应用“每个连接应用一个线程”的方式来实现TCP Server。但是在高负载的情况时,这种处理并不是很高效,因此需要应用其他的连接处理模型。在这篇文章中,将讨论怎么应用epoll来构建TCP Server的异步连接。

epoll是在Linux 2.6提出的新的系统调用,来代替被弃用的select(也包括poll)。select和poll的时间复杂度为O(n),而epoll的时间复杂度为O(1) - 这说明当被监听的描述符不断增加时,epoll仍然表现很好。select使用线性搜索方式,这导致它的时间复杂读为O(n),因此为了提高效率epoll则在内核文件架构中应用callback机制。

epoll中另一个根本的不同是epoll可以使用边沿触发,区别与水平触发。这说明当内核察觉文件描述符为I/O的就绪状态时你就会接受到“提示”(文件描述符可进行I/O操作的一瞬间),而不是被告知“描述符可以进行I/O操作”(只要文件描述符可以可进行I/O操作)。这样的方式由很多小优势:内核不必保持文件描述符的状态,而是给用户空间抛出异常,同时令用户空间的程序变得更灵活(如,可以忽略总是可读的状态)。

应用epoll方法你需要在程序中完成以下几个步骤:

  • 创建epoll调用的描述符:
    epfd = epoll_create(EPOLL_QUEUE_LEN);
    EPOLL_QUEUE_LEN是你想要同时管理的连接描述符的最大值。返回值epfd将在接下来的epoll调用中使用。这个描述符可以被关闭,当在后续程序中不需要使用时。

  • 完成了第一步后,你可以通过如下调用将需要监听的描述符添加到epoll中。

static struct epoll_event ev;
int client_sock;
...
ev.events = EPOLLIN | EPOLLPRI | EPOLLERR | EPOLLHUP;
ev.data.fd = client_sock;
int res = epoll_ctl(epfd, EPOLL_CTL_ADD, client_sock, &ev);

ev是epoll事件配置结构体,EPOLL_CTL_ADD - 预定义的常量以用来添加socket到epoll中。epoll_ctrl更详细的信息参考epoll_ctrl(2)的man页。当client_sock被关闭时,它将自动在epoll中被删除。

  • 当需要监听的描述符添加到epoll中后,你的程序将空闲或者等待以对epoll中socket进行操作。
while (1) {
    // wait for something to do...
    int nfds = epoll_wait(epfd, events, 
            MAX_EPOLL_EVENTS_PER_RUN, 
            EPOLL_RUN_TIMEOUT);
    if (nfds < 0) die("Error in epoll_wait!");

    // for each ready socket
    for(int i = 0; i < nfds; i++) {
        int fd = events[i].data.fd;
        handle_io_on_socket(fd);
    }
}

下面介绍了网络应用使用中的典型架构,这个架构在单进程和多进程中扩展性都非常好:

  • Listener - 在一个线程中执行bind()listen()调用,同时等待连接接入。当有连接接入后,这个线程可以在监听的socket上调用accept(),同时将已连入的连接发送给一个I/O-workers.
  • I/O-worker(s) - 一个或多个线程从linsener中接收连接并将他们添加到epoll中。
  • Data Processing worker(s) - 一个或多个线程从I/O-workers中写入或读取数据,并进行处理。

如你所见,epoll()API相当简单,但是却很强大。线性扩展将允许你管理大量的平行连接,这相比与一个连接一个进程来比,应用了更少的进程。

发布了37 篇原创文章 · 获赞 24 · 访问量 5万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章