http://www.cnblogs.com/venow/archive/2012/11/30/2790031.html

http://blog.csdn.net/denkensk/article/details/41978015

定义:

  epoll是Linux内核为处理大批句柄而作改进的poll,是Linux下多路复用IO接口select/poll的增强版本号。它能显著的降低程序在大量并发连接中仅仅有少量活跃的情况下的系统CPU利用率。

由于它会复用文件描写叙述符集合来传递结果而不是迫使开发人员每次等待事件之前都必须又一次准备要被侦听的文件描写叙述符集合。还有一个原因就是获取事件的时候,它无须遍历整个被侦听的描写叙述符集,仅仅要遍历那些被内核IO事件异步唤醒而增加Ready队列的描写叙述符集合即可了。epoll除了提供select\poll那种IO事件的电平触发(Level Triggered)外,还提供了边沿触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,降低epoll_wait/epoll_pwait的调用。提供应用程序的效率。

工作方式:

  LT(level triggered):水平触发。缺省方式,同一时候支持block和no-block socket。在这样的做法中,内核告诉我们一个文件描写叙述符是否被就绪了,假设就绪了,你就能够对这个就绪的fd进行IO操作。假设你不作不论什么操作。内核还是会继续通知你的,所以,这样的模式编程出错的可能性较小。

传统的select\poll都是这样的模型的代表。

  ET(edge-triggered):边沿触发,快速工作方式,仅仅支持no-block socket。在这样的模式下,当描写叙述符从未就绪变为就绪状态时,内核通过epoll告诉你。然后它会如果你知道文件描写叙述符已经就绪,而且不会再为那个描写叙述符发送很多其它的就绪通知,直到你做了某些操作导致那个文件描写叙述符不再为就绪状态了(比方:你在发送、接受或者接受请求。或者发送接受的数据少于一定量时导致了一个EWOULDBLOCK错误)。

可是请注意,如果一直不正确这个fs做IO操作(从而导致它再次变成未就绪状态)。内核不会发送很多其它的通知。

  差别:LT事件不会丢弃,而是仅仅要读buffer里面有数据能够让用户读取。则不断的通知你。而ET则仅仅在事件发生之时通知。

使用方式:

  1、int epoll_create(int size)

创建一个epoll句柄,參数size用来告诉内核监听的数目。

  2、int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

epoll事件注冊函数,

  參数epfd为epoll的句柄。

  參数op表示动作,用3个宏来表示:EPOLL_CTL_ADD(注冊新的fd到epfd),EPOLL_CTL_MOD(改动已经注冊的fd的监听事件),EPOLL_CTL_DEL(从epfd删除一个fd);

  參数fd为须要监听的标示符;

  參数event告诉内核须要监听的事件。event的结构例如以下:

    struct epoll_event {
      __uint32_t events; /* Epoll events */
      epoll_data_t data; /* User data variable */
    };


  当中events能够用下面几个宏的集合:

  EPOLLIN :表示相应的文件描写叙述符能够读(包含对端SOCKET正常关闭)

  EPOLLOUT:表示相应的文件描写叙述符能够写

  EPOLLPRI:表示相应的文件描写叙述符有紧急的数据可读(这里应该表示有带外数据到来)

  EPOLLERR:表示相应的文件描写叙述符错误发生

  EPOLLHUP:表示相应的文件描写叙述符被挂断。

  EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的

  EPOLLONESHOT:仅仅监听一次事件,当监听完这次事件之后。假设还须要继续监听这个socket的话。须要再次把这个socket增加到EPOLL队列里

3、 int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)

  等待事件的产生,类似于select()调用。

參数events用来从内核得到事件的集合。maxevents告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size,參数timeout是超时时间(毫秒。0会马上返回,-1将不确定,也有说法说是永久堵塞)。该函数返回须要处理的事件数目,如返回0表示已超时。

应用举例:

  以下,我引用google code中别人写的一个简单程序来进行说明。svn路径:http://sechat.googlecode.com/svn/trunk/

  该程序一个简单的聊天室程序。用Linux C++写的,server主要是用epoll模型实现。支持高并发,我測试在有10000个client连接server的时候。server处理时间不到1秒,当然client仅仅是与server连接之后,接受server的欢迎消息而已,并没有做其它的通信。

尽管程序比較简单。可是在我们考虑server高并发时也提供了一个思路。在这个程序中,我已经把全部的调试信息和一些与epoll无关的信息干掉。并加入必要的凝视,应该非常easy理解。

  程序共包括3个头文件和3个cpp文件。当中3个cpp文件里,每个cpp文件都是一个应用程序,server.cpp:server程序。client.cpp:单个client程序,tester.cpp:模拟高并发,开启10000个client去连server。

       utils.h头文件,就包括一个设置socket为不堵塞函数。例如以下:

    #include <fcntl.h>
    int setnonblocking(int sockfd)
    {
        CHK(fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0) | O_NONBLOCK));
        return 0;
    }


       local.h头文件,一些常量的定义和函数的声明,例如以下:



     head.h文件。程序中所要用到的头文件,例如以下:

    //#ifndef __HEAD__H__
    //#define __HEAD__H__
    #include <stdio.h>
    #include <iostream>
    #include <netinet/in.h>
    #include <arpa/inet.h>

    #include <sys/socket.h>

    #include <unistd.h>
    #include <sys/epoll.h>
    #include <stdlib.h>

    #include <list>
    #include <string.h>
    #include <iterator>
    #include <time.h>
    //#endif


server.cpp文件,epoll模型就在这里实现,例如以下:


tester.cpp文件,模拟server的高并发,开启10000个client去连接server。例如以下:


我就不给出程序的运行结果的截图了,只是以下这张截图是代码作者自己測试的。能够看出。并发10000无压力呀

(OK) Linux epoll模型—socket epoll server client chat

  单个客户端去连接server。client.cpp文件,例如以下:


源码下载:
linux-epoll-server-client-chat.tar.gz

[root@localhost test]# g++ server.cpp -o server
[root@localhost test]# g++ tester.cpp -o tester
[root@localhost test]# g++ client.cpp -o client


linux下输入命令ulimit -a,能够看到open files 一般时1024,所以未经改动时做本实验,最大打开到1023号。所以要改动。

改动方法:ulimit -n <能够同一时候打开的文件数目> 设置用户能够同一时候打开的最大文件数

假设本參数设置过小,对于并发訪问量大的站点,可能会出现too many open files 的错误。


相关文章: