epoll事件机制简介

浏览: 103 发布日期: 2017-07-01 分类: c++
epoll简介

a) epoll是对poll的改进;是linux2.6下性能最好的多路I/O就绪通知方法。相对于select和poll来说,epoll更加灵活并且不存在描述符限制。epoll使用一个文件描述符对其他的描述符进行管理;将用户所感兴趣的事件到内核进行注册,因此只需从用户态到内核态的一次copy,这与select、poll不同。而select、poll每次调用都要传递用户所有监控的socket给select/poll系统调用,这就意味着将用户态的socket列表拷贝到内核态。
b) 内核对于文件描述符集合的管理采用了红黑树,通过epoll_ctl对 红黑树上的文件描述符进行高效的管理;有效的对重复的注册信息 去重。
c) 此外,epoll维护一个双链表,用于存储已经就绪的文件描述符信息。当epoll_wait调用时,仅仅 这个list中是否存在数据即可。有数据就返回,没有数据就sleep,等timeout时即使链表没有数据也需要返回。通常情况下,监控大量的句柄但每次只有少量的就绪的句柄返回,因此只有少量的文件描述符从内核态拷贝到用户态。

函数介绍
int epoll_create(int)
int epoll_wait(int epollfd,struct epoll_event *events,int)
int epoll_ctl(int epollfd,int OPTION,int fd,struct epoll_event *ev)
struct epoll_event
{
    int events;
    epoll_data_t data;
};
typedef union epoll_data
{
    void *ptr ;
    int fd ;
    __uint32_t u32 ;
    __uint64_t u64 ;
}epoll_data_t;
OPTION : EPOLL_CTL_ADD/EPOLL_CTL_DEL/EPOLL_CTL_MOD
水平触发与边缘触发

a) 水平触发
LT(level triggered)是 epoll缺省工作方式,并且同时支持block 和no-block socket. 即:内核通知epoll_wait某个文件描述符已经就绪,然后你对此文件描述符进行处理;如果你不做处理的话,内核会继续通知 epoll_wait对此文件描述符进行处理;此方法不易出错。
b) 边缘触发
ET(edge triggered)是一种比较 高速的工作方式,只支持no-block socket. 相对于LT效率高。与LT的 区别是当内核通知epoll_wait一次后,不论用户对此文件描述符处理与否内核将不会对此文件描述符的事件再一次通知用户进行处理。也就是说,只通知用户一次。而LT会持续通知,直到处理为止。

服务端代码
#include
#include
#include
#include
#include

#include
#include
#include
#include
#include

#define IP "127.0.0.1"
#define PORT 9090
#define MAXLINE 1024
#define MAXLISTEN 5

/*
    @funcationname: createAndBind
    @description:create socket and bind the addresss
    @return
        succ: the valid of socket
        fail: -1
*/
int createAndBind()
{   
    int listenfd ;
    struct sockaddr_in servAddr ;

    bzero(&servAddr,sizeof(servAddr)) ;

    listenfd = socket(AF_INET,SOCK_STREAM,0) ;
    if (listenfd < 0)
    {
        printf("%s %d error create the listen socket,err:%d\n",__FILE__,__LINE__,errno) ;
        return -1 ;
    }

    servAddr.sin_family = AF_INET ;
    inet_pton(AF_INET,IP,&servAddr.sin_addr) ;
    servAddr.sin_port = htons(PORT);
    int ret = bind(listenfd,( struct sockaddr *)&servAddr,sizeof(servAddr)) ;
    if (ret < 0)
    {
        printf("%s %d, error bind the address,err:%d\n",__FILE__,__LINE__,errno) ;
        return -1 ;
    }

    ret = listen(listenfd,MAXLISTEN) ;
    if (ret < 0)
    {
        printf("%s %d, error listen \n",__FILE__,__LINE__) ;
        return -1 ;
    }

    int bReuseAddr = 1 ;
    ret = setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,(const char *)&bReuseAddr,sizeof(bReuseAddr)) ;
    if (ret < 0)
    {
        printf("%s %d,error setsockopt SO_REUSEADDR\n",__FILE__,__LINE__) ;
        return -1 ;
    }

    return listenfd ;
}

/*
    @functionname: epollAddEvent
    @decription:   add the fd's event
    @input  : 
        epollfd : epollfd
        clientfd: client socket
        state :   the event need to add to clientfd
    @return 
        succ : 0
        fail : -1
*/
int epollAddEvent(int epollfd,int clientfd,int state)
{
    struct epoll_event event ;
    event.data.fd = clientfd ;
    event.events = state ;

    return epoll_ctl(epollfd,EPOLL_CTL_ADD,clientfd,&event) ;
}

/*
    @functionname: epollModEvent
    @decription:   Modify the fd's event
    @input  : 
        epollfd : epollfd
        clientfd: client socket
        state :   the event need to modify clientfd
    @return 
        succ : 0
        fail : -1
*/
int epollModEvent(int epollfd,int clientfd,int state)
{
    struct epoll_event event ;
    event.data.fd = clientfd ;
    event.events = state ;

    return epoll_ctl(epollfd,EPOLL_CTL_MOD,clientfd,&event) ;
}

/*
    @functionname: epollDelEvent
    @decription:   Delete the fd's event
    @input  : 
        epollfd : epollfd
        clientfd: client socket
        state :   the event need to delete from clientfd
    @return 
        succ : 0
        fail : -1
*/
int epollDelEvent(int epollfd,int clientfd,int state)
{
    struct epoll_event event ;
    event.data.fd = clientfd ;
    event.events = state ;

    return epoll_ctl(epollfd,EPOLL_CTL_DEL,clientfd,&event) ;
}

/*
    @functionname: handlAccept
    @decription:   handle the listen socket
    @input  : 
        epollfd : epollfd
        listenfd: the listen socket 
    @return 
        succ : 0
        fail : -1
*/
int handleAccept(int epollfd,int listenfd)
{
    int connfd = -1 ;
    struct sockaddr_in cliAddr ;
    socklen_t cliAddrLen ;
    int ret = -1 ;

    //cliAddrLen = sizeof(cliAddr) ;

    memset(&cliAddr,0,sizeof(cliAddr)) ;

    connfd = accept(listenfd,(struct sockaddr*)&cliAddr,&cliAddrLen) ;
    if (connfd < 0)
    {
        printf("%s %d,error accept .err:%d\n",__FILE__,__LINE__,errno) ;
        return -1 ;
    }
    else
    {
        printf("%s,%d accept connfd is ok\n",__FILE__,__LINE__) ;
        ret = epollAddEvent(epollfd,connfd,EPOLLIN) ;
        if (ret < 0)
        {
            printf("%s %d, epollAddEvent is error\n",__FILE__,__LINE__) ;
            return -1 ;
        }
        return 0 ;
    }
}

char buf[MAXLINE] ;

int doRead(int epollfd,int cliConn)
{
    int nread  ;
    memset(buf,0,MAXLINE) ;
    nread = read(cliConn,buf,MAXLINE) ;
    if (nread < 0) //something is error
    {
        if (EINTR == errno)
        {
            printf("%s %d read is interrupt\n",__FILE__,__LINE__) ;
            return -1 ;
        }
        else
        {
            printf("%s %d,error read,err:%d\n",__FILE__,__LINE__,errno) ;
            close(cliConn) ;
            epollDelEvent(epollfd,cliConn,EPOLLIN) ;
            return -1 ;
        }
    }
    else if (nread==0) // the connection is close
    {
        printf("%s %d,the socket is closed ,err:%d\n",__FILE__,__LINE__,errno) ;
        close(cliConn) ;
        epollDelEvent(epollfd,cliConn,EPOLLIN) ;
        return 0 ;
    }
    else // read the data
    {
        buf[nread] = '\0' ;
        printf("recv message:%s\n",buf) ;
        epollModEvent(epollfd,cliConn,EPOLLOUT);
        return 0 ;
    }
}

int doWrite(int epollfd,int cliConn)
{
    int nWrite ;

    nWrite = write(cliConn,buf,strlen(buf)) ; // you can use writen
    if (nWrite < 0) //error
    {   
        if(errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) 
        {
            return -1 ;
        }   
        else
        {
            printf("%s %d write is error,%d\n",__FILE__,__LINE__,errno) ;
            close(cliConn) ;
            epollDelEvent(epollfd,cliConn,EPOLLOUT) ;
            return -1;
        }
    }
    else //write success
    {
        printf("%s,%d,write is ok\n",__FILE__,__LINE__) ;
        epollModEvent(epollfd,cliConn,EPOLLIN) ;
        return 0 ;
    }
}

/*
    @functionname: doEPoll
    @decription:   the main loop of epoll
    @input  : 
        epollfd : epollfd
        listenfd: listen socket
    @return 
        succ : 0
        fail : -1
*/
int doEpoll(int epollfd,int listenfd)
{
    struct epoll_event events[MAXLINE] ;
    int ret = -1 ;
    int i = 0 ;

    for(;;)
    {
        ret = epoll_wait(epollfd,events,MAXLINE,-1) ; // it is not weekup util someone's event is active or the timeout is reached!
        if (ret < 0)
        {
            if (EINTR == errno)
            {
                printf("%s %d, epoll is interrupt:%d\n",__FILE__,__LINE__,errno) ;
                continue ;
            }
            else
            {
                close(epollfd) ;
                return -1 ;
            }
        }
        else if (ret == 0) //timeout
        {
            printf("%s %d,epoll is timeout\n",__FILE__,__LINE__) ;
            continue ;
        }
        else //ok
        {
            for (i = 0 ;i
客户端代码
#include
#include
#include

#include
#include
#include
#include
#include
#include
#include

#define IP "127.0.0.1"
#define PORT 9090
#define MAXLINE 1024



int handleConnection(int sockfd)
{

    char buf[MAXLINE] ="hello Mr.Luo!" ;
    int ret = write(sockfd,buf,strlen(buf)) ;
    if (ret < 0)
    {
        printf("error:%d\n",errno) ;
        close(sockfd) ;
        return -1 ;
    }

    ret = read(sockfd,buf,MAXLINE) ;
    buf[ret] = '\0' ;
    printf("recv message :%s\n",buf) ;
    return 0 ;

}


int main()
{
    int sockfd ;
    struct sockaddr_in servAddr ;

    sockfd = socket(AF_INET,SOCK_STREAM,0) ;

    bzero(&servAddr,sizeof(servAddr) ) ;
    servAddr.sin_family= AF_INET ;
    inet_pton(AF_INET,IP,&servAddr.sin_addr) ;
    servAddr.sin_port = htons(PORT) ;

    int ret = connect(sockfd,(struct sockaddr*)&servAddr,sizeof(servAddr)) ;
    if (ret < 0)
    {
        printf("close the socket,when connect is failed\n") ;
        close(sockfd) ;
        exit(-1) ;
    }

    handleConnection(sockfd) ;
    close(sockfd) ;

    return 0 ;
}


参考文献 epoll详解 IO多路复用之epoll总结 以上就是epoll事件机制简介的全文介绍,希望对您学习和使用c++编程开发有所帮助.
返回顶部