`
zuroc
  • 浏览: 1307825 次
  • 性别: Icon_minigender_1
  • 来自: 江苏
社区版块
存档分类
最新评论

[摘录]epoll学习笔记

阅读更多
注:内容皆是摘录

Linux 2.6内核完全支持epoll.

epoll的IO效率不随FD数目增加而线性下降

传统的select/poll每次调用都会线性扫描全部的集合,导致效率呈现线性下降。

内核实现中epoll是根据每个fd上面的callback函数实现的。只有"活跃"的socket才会主

动的去调用 callback函数,其他idle状态socket则不会。

如果所有的socket基本上都是活跃的---比如一个高速LAN环境,过多使用epoll,效率

相比还有稍微的下降。

但是一旦使用idle connections模拟WAN环境,epoll的效率就远在select/poll之上了。


poll的执行分三部分:
1.将用户传入的pollfd数组拷贝到内核空间,因为拷贝操作和数组长度相关,时间

上这是一个O(n)操作

2.
查询每个文件描述符对应设备的状态,如果该设备尚未就绪,则在该设备的等

待队列中加入一项并继续查询下一设备的状态。
查询完所有设备后如果没有一个设备就绪,这时则需要挂起当前进程等待,直

到设备就绪或者超时。
设备就绪后进程被通知继续运行,这时再次遍历所有设备,以查找就绪设备。
这一步因为两次遍历所有设备,时间复杂度也是O(n),这里面不包括等待时

间。。 

3.
将获得的数据传送到用户空间并执行释放内存和剥离等待队列等善后工作,向

用户空间拷贝数据与剥离等待队列等操作的的时间复杂度同样是O(n)。

epoll用到的所有函数都是在头文件sys/epoll.h中声明的,下面简要说明所用到的数

据结构和函数:
所用到的数据结构
typedef union epoll_data {
                void *ptr;
                int fd;
                __uint32_t u32;
                __uint64_t u64;
        } epoll_data_t;

        struct epoll_event {
                __uint32_t events;      /* Epoll events */
                epoll_data_t data;      /* User data variable */
        };
结构体epoll_event 被用于注册所感兴趣的事件和回传所发生待处理的事件.

其中epoll_data 联合体用来保存触发事件的某个文件描述符相关的数据.

例如一个client连接到服务器,服务器通过调用accept函数可以得到于这个client对

应的socket文件描述符,可以把这文件描述符赋给epoll_data的fd字段以便后面的读

写操作在这个文件描述符上进行。epoll_event 结构体的events字段是表示感兴趣的

事件和被触发的事件可能的取值为:
EPOLLIN :表示对应的文件描述符可以读;
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET:表示对应的文件描述符有事件发生;



所用到的函数:
1、int epoll_create(int size)
   该函数生成一个epoll专用的文件描述符,其中的参数是指定生成描述符的最大

范围
   
2、
用于控制某个文件描述符上的事件,可以注册事件,修改事件,删除事件。
如果调用成功返回0,不成功返回-1
int epoll_ctl(
    int epfd,//由 epoll_create 生成的epoll专用的文件描述符
    int op,//要进行的操作例如注册事件,可能的取值EPOLL_CTL_ADD 注册、

EPOLL_CTL_MOD 修改、EPOLL_CTL_DEL 删除
    int fd,//关联的文件描述符
    struct epoll_event *event//指向epoll_event的指针
    )
   
3、
用于轮询I/O事件的发生
返回发生事件数
int epoll_wait(
    int epfd,//由epoll_create 生成的epoll专用的文件描述符
    struct epoll_event * events,//用于回传代处理事件的数组
    int maxevents,//每次能处理的事件数
    int timeout//等待I/O事件发生的超时值
               //为0的时候表示马上返回,为-1的时候表示一直等下去,直到有事件
               //为任意正整数的时候表示等这么长的时间,如果一直没有事件
               //一般如果网络主循环是单独的线程的话,可以用-1来等,这样可以

保证一些效率
               //如果是和主逻辑在同一个线程的话,则可以用0来保证主循环的效率


    )

用法示例
       struct epoll_event ev, *events;

       for(;;) {
           nfds = epoll_wait(kdpfd, events, maxevents, -1);/*wait for an I/O event. */

           for(n = 0; n < nfds; ++n) {
               if(events[n].data.fd == listener) {/*if listen socket has an I/O, accept the new connect*/
                   client = accept(listener, (struct sockaddr *) &local,
                                   &addrlen);
                   if(client < 0){
                       perror("accept");
                       continue;
                   }
                   setnonblocking(client);
                   ev.events = EPOLLIN | EPOLLET;/*EPOLLIN-available for read*/
                   ev.data.fd = client;
                   if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0) {/*add the new socket into the 

epoll file descriptors*/
                       fprintf(stderr, "epoll set insertion error: fd=%d\n",
                               client);
                       return -1;
                   }
               }
               else
                   do_use_fd(events[n].data.fd);/*read from a socket which has data come*/
           }
       }

代码2

static int        s_epfd;//epoll描述字

{//初始化epoll
        struct epoll_event        ev;

        //设置epoll
        s_epfd = epoll_create(65535);

        {//这个过程可以循环以便加入多个LISTEN套接字进入epoll事件集合
                //服务器监听创建
                rc = listen();//listen参数这里省略

                //加入epoll事件集合
                ev.events = EPOLLIN;
                ev.data.fd = rc;
                if (epoll_ctl(s_epfd, EPOLL_CTL_ADD, rc, &ev) < 0) {
                        fprintf(stderr, "epoll set insertion error: fd=%d", rc);
                        return(-1);
                }
        }
}

{//epoll事件处理
        int        i, nfds, sock_new;
        struct epoll_event        events[16384];
        for( ; ; ) {
                //等待epoll事件
                nfds = epoll_wait(s_epfd, events, 16384, -1);
                //处理epoll事件
                for(i = 0; i < nfds; i++) {
                        //events[i].data.fd是epoll事件中弹出的套接字
                        //接收连接
                        sock_new = accept(events[i].data.fd);//accept其它参数这里省略了
                        if(0 > sock_new) {
                                fprintf(stderr, "接收客户端连接失败\n");
                                continue;
                        }
                }
        }
}



代码3
#include <iostream>

#include <sys/socket.h>

#include <sys/epoll.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <fcntl.h>

#include <unistd.h>

#include <stdio.h>

 

#define MAXLINE 10

#define OPEN_MAX 100

#define LISTENQ 20

#define SERV_PORT 5555

#define INFTIM 1000

 

void setnonblocking(int sock)

{

     int opts;

     opts=fcntl(sock,F_GETFL);

     if(opts<0)

     {

          perror("fcntl(sock,GETFL)");

          exit(1);

     }

     opts = opts|O_NONBLOCK;

     if(fcntl(sock,F_SETFL,opts)<0)

     {

          perror("fcntl(sock,SETFL,opts)");

          exit(1);

     }   

}

 

int main()

{

     int i, maxi, listenfd, connfd, sockfd,epfd,nfds;

     ssize_t n;

     char line[MAXLINE];

     socklen_t clilen;

     //声明epoll_event结构体的变量,ev用于注册事件,数组用于回传要处理的事件

     struct epoll_event ev,events[20];

     //生成用于处理accept的epoll专用的文件描述符

     epfd=epoll_create(256);

 

     struct sockaddr_in clientaddr;

     struct sockaddr_in serveraddr;

     listenfd = socket(AF_INET, SOCK_STREAM, 0);

     //把socket设置为非阻塞方式

     setnonblocking(listenfd);

     //设置与要处理的事件相关的文件描述符

     ev.data.fd=listenfd;

     //设置要处理的事件类型

     ev.events=EPOLLIN|EPOLLET;

     //注册epoll事件

     epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);

 

     bzero(&serveraddr, sizeof(serveraddr));

     serveraddr.sin_family = AF_INET;

 

     char *local_addr="200.200.200.204";

     inet_aton(local_addr,&(serveraddr.sin_addr));//htons(SERV_PORT);

     serveraddr.sin_port=htons(SERV_PORT);

     bind(listenfd,(sockaddr *)&serveraddr, sizeof(serveraddr));

     listen(listenfd, LISTENQ);

 

     maxi = 0;

     for ( ; ; ) {

          //等待epoll事件的发生

          nfds=epoll_wait(epfd,events,20,500);

          //处理所发生的所有事件     

          for(i=0;i<nfds;++i)

          {

               if(events[i].data.fd==listenfd)

               {

 

                    connfd = accept(listenfd,(sockaddr *)&clientaddr, &clilen);

                    if(connfd<0){

                         perror("connfd<0");

                         exit(1);

                    }

                    setnonblocking(connfd);

 

                    char *str = inet_ntoa(clientaddr.sin_addr);

                    std::cout<<"connect from "<_u115 ?tr<<std::endl;

                    //设置用于读操作的文件描述符

                    ev.data.fd=connfd;

                    //设置用于注测的读操作事件

                    ev.events=EPOLLIN|EPOLLET;

                    //注册ev

                    epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);

               }

               else if(events[i].events&EPOLLIN)

               {

                    if ( (sockfd = events[i].data.fd) < 0) continue;

                    if ( (n = read(sockfd, line, MAXLINE)) < 0) {

                         if (errno == ECONNRESET) {

 

                              close(sockfd);

                              events[i].data.fd = -1;

                         } else

                              std::cout<<"readline error"<<std::endl;

                    } else if (n == 0) {

                         close(sockfd);

                         events[i].data.fd = -1;

                    }

                    //设置用于写操作的文件描述符

                    ev.data.fd=sockfd;

                    //设置用于注测的写操作事件

                    ev.events=EPOLLOUT|EPOLLET;

                    //修改sockfd上要处理的事件为EPOLLOUT

                    epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);

               }

               else if(events[i].events&EPOLLOUT)

               {   

                    sockfd = events[i].data.fd;

                    write(sockfd, line, n);

                    //设置用于读操作的文件描述符

                    ev.data.fd=sockfd;

                    //设置用于注测的读操作事件

                    ev.events=EPOLLIN|EPOLLET;

                    //修改sockfd上要处理的事件为EPOLIN

                    epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);

               }

 

          }

 

     }

}
分享到:
评论

相关推荐

    epoll学习笔记.doc

    本笔记将深入探讨`epoll`的工作原理、两种主要模式——Edge Triggered (ET) 和 Level Triggered (LT),以及如何在实际编码中使用`epoll`。 首先,`epoll`相比于传统的`select`和`poll`,提供了更高的性能和更灵活的...

    epoll学习资料

    最后,`分别使用epoll、libevent实现的端口映射程序,学习epoll,libevent.zip`包含了使用Epoll和libevent实现的端口映射程序,有助于比较两种I/O多路复用技术的差异。 综上所述,Epoll是Linux环境下实现高性能网络...

    epoll学习实例

    epoll学习实例,epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是...

    unbound + libevent + epoll学习.txt

    标题与描述中的“unbound + libevent + epoll学习”指向了深入探讨三个关键概念:Unbound(一个DNS解析器),Libevent(一个事件驱动库),以及epoll(一种高效的I/O多路复用机制)。这三者在现代网络编程、尤其是高...

    epoll 使用golang实现

    在IT领域,`epoll`是一种高效的I/O事件通知机制,尤其在处理大量并发连接时,它是Linux系统下的首选方案。`epoll`利用内核与用户空间的共享数据结构来减少上下文切换,提高了系统在高并发环境下的性能。本篇文章将...

    linux epoll多线程编程 例子

    在Linux系统中,epoll是I/O多路复用技术的一种高效实现,它极大地改进了传统的轮询方式,尤其在处理大量并发连接时表现出色。本文将深入探讨如何在多线程环境中使用epoll进行程序设计,以提高系统的并行处理能力。 ...

    Linux C++ epoll使用范例

    在Linux系统中,`epoll`是用于I/O...综上所述,"Linux C++ epoll使用范例"中的代码可以作为学习和实践`epoll`的参考,通过客户端、服务端及测试程序,读者可以深入理解`epoll`的工作机制,并能实际操作中提升系统性能。

    epoll机制epoll_create、epoll_ctl、epoll_wait、close(在epoll的ET模式下,read和write或send和recv当返回值0且errno=EAGAIN - linking530的专栏 - CSDN博客.mht

    epoll机制epoll_create、epoll_ctl、epoll_wait、close(在epoll的ET模式下,read和write或send和recv当返回值0且errno=EAGAIN - linking530的专栏 - CSDN博客.mht

    Linux QT Epoll 服务器Demo

    这个Demo对于学习如何在Linux环境下使用QT结合Epoll开发高并发服务器具有很高的参考价值。通过理解Epoll的工作原理和QT的网络编程模型,开发者可以构建出性能优异、能够应对大量并发连接的服务器应用。

    epoll内核代码学习

    【epoll内核代码学习】 epoll是一种高效、可扩展的I/O事件通知机制,用于Linux内核中,尤其适用于高并发的网络编程。本文将深入解析epoll的内核实现,探讨其核心数据结构、关键操作以及与select的对比。 ### 第一...

    Lua的epoll模块lua-epoll.zip

    Lua的epoll模块 更多细节,请查看sample.lua API: ok,err=epoll.setnonblocking(fd) 设置一个文件描述符非阻塞。 epfd,err=epoll.create() 创建一个 epoll 文件描述符。ok,err=epoll.register(epfd,fd,event...

    epoll模型的一个例子

    在Linux系统编程中,`epoll`是一种高效、可扩展的I/O多路复用技术,主要用于处理大量的并发连接。`epoll`模型是解决高并发服务器性能瓶颈的有效手段之一,它比传统的`select`和`poll`模型更加先进,能够更有效地管理...

    epoll demo(LT和ET)

    **epoll**是Linux内核提供的一种高效I/O事件通知机制,它替代了传统的`poll`和`select`,在高并发I/O处理场景...学习和掌握`epoll`,尤其是其两种工作模式,对于提升服务器性能和处理高并发I/O场景有着至关重要的作用。

    linux socket tcp大并发 epoll使用教程 有关epoll的一切

    linux socket tcp大并发 epoll使用教程 有关epoll的一切

    linux epoll的封装类

    通过阅读和学习这个代码,初学者不仅能了解epoll的基本用法,还能深入理解如何在实际项目中优雅地封装和使用系统调用,提升系统编程能力。同时,对于开发者来说,这样的封装也便于代码的维护和复用。

    epoll 精髓

    epoll是Linux内核为处理大量文件描述符而提供的一种高效的事件通知机制,它在Linux网络编程中被广泛使用,尤其在处理成千上万的连接时,性能远胜于select和poll这两种传统的I/O事件通知方法。 select方法的缺点在于...

    epoll回射服务器

    总之,epoll回射服务器是一个实用的学习项目,它涵盖了网络编程的核心概念,如套接字、I/O多路复用以及C++的内存管理和异常处理。初学者可以通过这个项目深入理解epoll的工作原理,并为更复杂的网络应用打下基础。

    epoll_serverANDclient

    **epoll_serverANDclient**项目提供了...通过这个项目,我们可以学习如何在C++中使用`epoll`实现高性能的客户端和服务器,理解其在并发I/O处理中的优势。在实际开发中,这种技术常用于构建高可用、高性能的网络服务。

    python-epoll-examples

    总之,"python-epoll-examples"这个压缩包是学习Python在高并发场景下使用epoll的宝贵资源,通过实践这些示例,你将能够熟练掌握epoll的使用,提高你的网络编程能力。在实际项目中,结合epoll的高效特性,可以构建出...

    linux epoll 的实现

    Linux下的epoll是一种高效、可扩展的I/O多路复用技术,主要用于处理大量并发连接。它是基于事件驱动的I/O模型,适用于高并发服务器,如Web服务器、...在`epoll.cpp`代码中,我们可以深入学习如何在实践中应用这些概念。

Global site tag (gtag.js) - Google Analytics