epoll介绍和使用

来源:互联网 发布:网络桥架 编辑:程序博客网 时间:2024/06/15 22:08

epoll是Linux在2.6内核新增的系统调用,为了更高效地实现多路IO复用。与poll和select相比,它的高效体现在

1、 select和poll都是线性扫描FD(文件描述符)的集合,随着集合的增大,性能自然下降,且不能通过返回值得知那些文件描述符有I/O事件发生,还要再便利一边集合才能找出有I/O事件的文件描述符。而epool只是管理活跃的I/O的FD,不会因为集合增大而性能下降。

2、 select和poll是通过内存拷贝的方式,把FD消息从内核空间拷贝到用户空间,效率地下。epoll是通过共享内存方式。

epool的使用也比较简单。说明如下:

1、创建epoll句柄。

int epoll_create(int size);
  • 1
  • 1

参数size是监听句柄的最大数目,它的大小和机器内存有关。返回值是创建的epoll句柄。句柄使用完后调用close()关闭,因为它占用系统的FD。创建epoll句柄的本质是向内核申请空间,用来存放关注的FD集合以及事件。

2、epoll事件注册函数

int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event)
  • 1
  • 1

注册想要监听的事件类型。参数epfd是epoll_create创建的句柄。

参数op表示动作,在使用时用三个宏来表示: 
EPOLL_CTL_ADD表示注册新的fd到epfd中。 
EPOLL_CTL_MOD表示修改已经注册过的fd的监听事件。 
EPOLL_CTL_DEL表示从epf中删除fd。

参数fd是要监听的fd。

参数event是向内核注册的监听事件。struct epoll_event结构如下:

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 */};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

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)
  • 1
  • 1

参数epfd是epoll_create创建的句柄。参数events是从内核得到的事件的集合。参数maxevents告诉内核events的大小,它可以大于epoll_create时指定的size的大小。参数timeout是超时事件,单位为毫秒。

函数返回需要处理的事件的个数,返回0则表示已超时。

epoll事件的触发有两种模式,边沿触发Edge Triggered(ET)和水平Level Triggered(LT)。

ET模式:边沿触发是指当监听的FD状态变换时获得通知。例如接收数据的FD缓存一次接收了2k数据,但是读取时只读取了1k,缓存还有1k数据,但是不会再获得通知。所以采用边沿触发时,如果读取的数据长度等于设定值的话就要继续读取,直到产生EAGAIN。ET模式只支持非阻塞的I/O,以防止一个句柄的读/写操作把处理多个文件描述符的任务饿死。

LT模式:水平触发是默认的模式,它支持阻塞和非阻塞I/O。当I/O就绪时,就会获得通知,如果不对I/O进行处理或没有处理完,内核就会一直通知。

下面结合网上例子,用epoll写一个Echo服务器。实现很简单,把accept、recv、send的I/O操作放到epoll中,有了I/O事件后调用相应的回调函数即可。具体步骤如下:

1、初始化监听FD,把accept事件作为回调函数 ‘AcceptConnection’,等待客户端connect后在服务的调用。

2、在回调函数’ AcceptConnection’中,把accept后的FD注册为等待EPOLLIN,即等待客户端send操作,回调函数为’RecvData‘。

3、客户端写操作(send)后,服务端调用了’RecvData’,在这个函数中,把对应的FD的I/O事件改为EPOLLOUT,回调函数为’SendData’,因为下一步就是向客户端写收到的数据了。

4、在’SendData’函数中,发送接收到的数据,并把对应FD等待事件改为EPOLLIN,重新等待客户端的send操作。

在实现时,用到了封装的数据结构myevent,目的是为了便于操作事件,把事件的回调函数、buffer、上一次激活事件等做了封装。并使用了全局数组集合来作为事件的集合。

代码如下:

//EchoEpoll.cpp#include <iostream>#include <sys/epoll.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <fcntl.h>#include <unistd.h>#include <stdio.h>#include <errno.h>#include <string.h> //bzero Function#include <stdlib.h>//atoi()#define MAX_EVENTS 1000struct myevent{public:    int fd;    void (*callBack)(int fd, int events, void* arg);    int events;    void* arg;    bool status;//ture represent it has been added to epoll    char buffer[128];    int len;//use length    int offset;//mark from where to send    long  lastActiveTime;};//Global variablesint g_epollFd;//epoll fdmyevent g_Events[MAX_EVENTS+1];//event set. The last one is used to accept//Set event void SetEvent(myevent* ev, int fd, void(*callBack)(int, int , void*), void* arg){    ev->fd=fd;    ev->callBack=callBack;    ev->events=0;    ev->arg=arg;    ev->status=false;    bzero(ev->buffer,sizeof ev->buffer);    ev->offset=0;    ev->len=0;    ev->lastActiveTime=time(NULL);}//only set event's callback function, don't set it's buffervoid SetEventCallback(myevent* ev, void(*callBack)(int, int ,void*)){    ev->callBack=callBack;}void AddEvent(int epfd, int events, myevent* ev){    struct epoll_event epv={0,{0}};    epv.data.ptr=ev;    epv.events=ev->events=events;    int op;    if(ev->status)// have been added        op=EPOLL_CTL_MOD;    else    {        op=EPOLL_CTL_ADD;        ev->status=true;    }    if(epoll_ctl(epfd,op,ev->fd,&epv)<0)        printf("Event add failed  FD=%d,events=%d\n",ev->fd,events);    else        printf("Event add successfully,FD=%d,op=%d,events=%0x\n",ev->fd,op,events);}void DelEvent(int epfd, myevent* ev){    struct epoll_event epv={0,{0}};    if(!ev->status)//have not been added        return;    epv.data.ptr=ev;    ev->status=false;    epoll_ctl(epfd,EPOLL_CTL_DEL,ev->fd,&epv);}//forward declationvoid RecvData(int fd, int events, void* arg);void SendData(int fd, int events, void* arg);void AcceptConnection(int fd, int events, void* arg){    struct sockaddr_in sin;    socklen_t len=sizeof(struct sockaddr_in);    int nfd=accept(fd,(struct sockaddr*)&sin,&len) ;    if(nfd==-1)    {        if(errno!= EAGAIN&&errno!=EINTR)        {}        printf("%s, accept, %d",__func__,errno);    }    int i;    do    {        for( i=0; i<MAX_EVENTS;++i)//find the first unused Event        {            if(g_Events[i].status==false)                break;        }        if(i==MAX_EVENTS)        {            printf("%s:max connection limit %d\n",__func__,MAX_EVENTS );            break;//break do-while        }        int iret=fcntl(nfd,F_SETFL, O_NONBLOCK);        if(iret<0)        {            printf("%s, fctl nonblocking failed:%d",__func__, iret);            break;//break do-while        }        //set nfd, wait to receive data        SetEvent(&g_Events[i], nfd, RecvData,&g_Events[i]);        AddEvent(g_epollFd,EPOLLIN,&g_Events[i]);    }while(0);    printf("new connection[%s:%d] [time:%d], pos[%d]\n",inet_ntoa(sin.sin_addr),ntohs(sin.sin_port),g_Events[i].lastActiveTime,i);}void RecvData(int fd, int events, void* arg){    struct  myevent* ev=(struct myevent*)arg;    int len=recv(fd,ev->buffer+ev->len,sizeof(ev->buffer)-1-ev->len,0);    //has been received data, delete recv event    DelEvent(g_epollFd,ev);    if(len>0)    {        ev->len+=len;        ev->buffer[len]='\0';        printf("C[%d]:%s\n",fd,ev->buffer);        SetEventCallback(ev,SendData);        //ev->callBack=SendData;        AddEvent(g_epollFd,EPOLLOUT,ev);    }    else if(len==0)    {        close(ev->fd);        printf("[fd=%d] pos[%d], closed gracefully.\n",fd, ev->events);    }    else    {        close(ev->fd);        printf("recv[fd=%d] errno[%d]:%s\n",fd,errno,strerror(errno));    }}void SendData(int fd, int events, void* arg){    struct myevent* ev=(struct myevent*)arg;    int len=send(fd, ev->buffer+ev->offset, ev->len-ev->offset, 0);    if(len>0)    {        printf("send [fd=%d],[%d<->%d]%s\n",fd,len,ev->len,ev->buffer);        ev->offset+=len;        if(ev->offset==ev->len)// all data has been send        {            DelEvent(g_epollFd,ev);            SetEvent(ev,fd,RecvData,ev);//reset event            AddEvent(g_epollFd,EPOLLIN,ev);        }    }    else    {        close(ev->fd);        DelEvent(g_epollFd,ev);        printf("send[fd=%d] errno[%d]\n",fd,errno);    }}void InitListenSocket(int epfd, short port){    int listenFd=socket(AF_INET, SOCK_STREAM,0);    fcntl(listenFd, F_SETFL, O_NONBLOCK);//Set listen socket to Non-block    printf("server listen fd=%d\n",listenFd);    SetEvent(&g_Events[MAX_EVENTS], listenFd, AcceptConnection, &g_Events[MAX_EVENTS]);    AddEvent(epfd, EPOLLIN,&g_Events[MAX_EVENTS]);    sockaddr_in sin;    bzero(&sin, sizeof(sin));    sin.sin_family=AF_INET;    sin.sin_addr.s_addr=INADDR_ANY;    sin.sin_port=htons(port);    bind(listenFd,(const sockaddr*)&sin, sizeof(sin));    listen(listenFd,MAX_EVENTS);}int main(int argc, char* argv[]){    unsigned short port=12345;    if(argc==2)    {        port=atoi(argv[1]);    }    g_epollFd=epoll_create(MAX_EVENTS);    if(g_Events<=0)        printf("Create epoll failed.\n");    InitListenSocket(g_epollFd,port);    printf("Server running:port %d\n",port);    struct epoll_event events[MAX_EVENTS];    int checkPos=0;    while(true)    {        long now=time(NULL);        for(int i=0; i< 100; ++i, checkPos++)        {            if(checkPos==MAX_EVENTS)                checkPos=0;            if(g_Events[checkPos].status==false)                continue;            long duration=now - g_Events[checkPos].lastActiveTime;            //close time out fd            if(duration>=60)//timeout            {                close(g_Events[checkPos].fd);                printf("fd=%d timeout:%d--%d\n",g_Events[checkPos].fd, g_Events[checkPos].lastActiveTime , now);                DelEvent(g_epollFd,&g_Events[checkPos]);            }        }        int fds=epoll_wait(g_epollFd,events,MAX_EVENTS,1000);        if(fds<0)        {            printf("epoll_wait error\n");            break;        }        for(int i=0; i<fds;++i)        {            myevent* ev=(struct myevent*)events[i].data.ptr;            if((events[i].events&EPOLLIN)&&(ev->events&EPOLLIN))            {                ev->callBack(ev->fd, events[i].events, ev->arg);            }            if((events[i].events&EPOLLOUT)&&(ev->events&EPOLLOUT))            {                ev->callBack(ev->fd, events[i].events, ev->arg);            }        }    }    return 0;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253

测试客户端如下:

//EchoClient.cpp#include <stdio.h>#include <sys/types.h>#include <sys/socket.h>#include <unistd.h>#include <stdlib.h>#include <errno.h>#include <arpa/inet.h>#include <netinet/in.h>#include <string.h>int main(int argc, char* argv[]){    int fd=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);    if(fd<0)    {        perror("Create Scoket Error\n");        exit(EXIT_FAILURE);    }    struct sockaddr_in serverAddr;    bzero(&serverAddr, sizeof serverAddr);    serverAddr.sin_family=AF_INET;    serverAddr.sin_port=htons(12345);    serverAddr.sin_addr.s_addr=inet_addr("127.0.0.1");    if(connect(fd, (struct sockaddr*)&serverAddr, sizeof serverAddr)<0)    {        perror("Connect Error\n");        exit(EXIT_FAILURE);    }    char sendBuf[128]={0};    char recvBuf[128]={0};    while(fgets(sendBuf, sizeof sendBuf, stdin) != NULL)    {        write(fd, sendBuf, strlen(sendBuf));        read(fd, recvBuf, sizeof recvBuf);        fputs(recvBuf, stdout);        bzero(sendBuf, sizeof sendBuf);        bzero(recvBuf, sizeof recvBuf);    }    close(fd);    return 0;}
原创粉丝点击