码迷,mamicode.com
首页 > 其他好文 > 详细

I/O多路转接之epoll

时间:2016-07-20 23:09:17      阅读:314      评论:0      收藏:0      [点我收藏+]

标签:epoll

一、对epoll的初步认识

    epoll是为了处理大量句柄而对poll做了改进。

    epoll的相关系统调用:

    (1)iint epoll_create(int size)

            创建一个epoll的句柄。size通常是被忽略的。当创建epoll句柄后,它就会占用一个fd值,所以在使用完epoll后,必须调用close()进行关闭,否则可能导致fd被耗尽。

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

            epoll的事件注册函数,它不同于select()是在监视事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。

            第一个参数是epoll_create()的返回值;

            第二个参数表示动作,用三个宏来表示:

            EPOLL_CTL_ADD:注册新的fd到epfd中;

            EPOLL_CTL_MOD:修改已经注册的fd的监听事件;

            EPOLL_CTL_DEL:从epfd中删除一个fd;

            第三个参数是需要监听的fd。

            第四个参数是告诉内核需要监听什么事,struct epoll_event结构如下:

技术分享            events可以是以下几个宏的集合:

            EPOLLIN:表示对应的文件描述符可以读。

            EPOLLOUT:表示对应的文件描述符可以写。

            EPOLLPRI:表示对应的文件描述符有紧急的数据可读。

            EPOLLERR:表示对应的文件描述符发生错误。

            EPOLLHUP:表示对应的文件描述符被挂断。

            EPOLLLET:将EPOLL设为边缘触发模式(ET)。这里是相对于水平触发(LT)来说的。

            EPOLLONESHOT:只监听一次事件。当监听完这个事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到epoll队列中。

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

             收集在epoll监控的事件中已经发送的事件。参数events是分配好的epoll_event结构体数组。epoll将会把发生的事件赋值到events数组中(evevts不可以是空指针,内核只负责把数据复制到evevts数组中,并不会帮助我们在用户态中分配内存)。maxevents告知内核这个events有多大,这个maxevents的值不能大于epoll_create()时的size。参数timeout是超时时间,如果函数调用成功,返回对应I/O已准备好的文件描述符数目,如果返回0表示已超时。

二、epoll的工作原理

        epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表描述符数量的值,我们只需要在epoll指定的一个数组中依次取得相应数量的文件描述符即可。这里也使用了内存映射技术,这里也省掉了这些描述符在系统调用时复制的开销。

        epoll采用基于事件的就绪通知方式。在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl的回掉机制,迅速激活这个文件描述符,当进程调用epoll_wait时便得到通知。

三、水平触发(LT)和边缘触发 (ET)

(1)Level Triggered工作模式

    以LT调用epoll接口的时候,就相当于一个速度比较快的poll(2)。LT是epoll的缺省工作方式,同时支持阻塞和非阻塞。在这种做法中,内核告诉你一个文件描述符是否就绪了,然后再对这个就绪的fd进行IO操作,当不做任何操作的时候,内核还是会继续通知你的。所以,这种模式编程出错的可能性要小一些,传统的select/poll就是这种模型的代表。

代码描述:

  1 #include<stdio.h>                                                           
  2 #include<stdlib.h>
  3 #include<sys/epoll.h>
  4 #include<sys/types.h>
  5 #include<sys/socket.h>
  6 #include<netinet/in.h>
  7 #include<arpa/inet.h>
  8 #include<unistd.h>
  9 #include<fcntl.h>
 10 #include<assert.h>
 11 #include<errno.h>
 12 #include<string.h>
 13 
 14 #define _BACKLOG_ 5
 15 #define _SIZE_ 64
 16 #define _MAX_FD_SIZE_ 64
 17 #define _BUF_SIZE_ 10240
 18 
 19 typedef struct data_buf
 20 {
 21     int fd;
 22     char buf[_BUF_SIZE_];
 23 }data_buf_t,*data_buf_p;
 24 
 25 static void Usage(char* const proc)
 26 {
 27     assert(proc);
 28     printf("%s [ip][port]\n",proc);
 29 }
 30 
 31 static int startup(char *ip,int port)
 32 {
 33     assert(ip);
 34     int sock=socket(AF_INET,SOCK_STREAM,0);
 35     if(sock<0)
 36     {
 37         perror("socket");
 38         exit(1);
 39     }
 40     
 41     int opt=1;
 42     setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
 43 
 44     struct sockaddr_in local;
 45     local.sin_family=AF_INET;
 46     local.sin_port=htons(port);
 47     local.sin_addr.s_addr=inet_addr(ip);
 48 
 49     if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
 50     {
 51         perror("bind");
 52         exit(2);
 53     }
 54     if(listen(sock,_BACKLOG_)<0)
 55     {
 56         perror("listen");
 57         exit(3);
 58     }
 59     return sock;
 60 }
 61 
 62 //sock=listen_sock
 63 static int server_epoll(int sock)
 64 {
 65     int epoll_fd=epoll_create(_SIZE_);
 66     if(epoll_fd<0)
 67     {     
 68         perror("epoll_create");
 69         return -1;
 70     }
 71 
 72     struct epoll_event ev;
 73     ev.data.fd=sock;
 74     ev.events=EPOLLIN;
 75     if(epoll_ctl(epoll_fd,EPOLL_CTL_ADD,sock,&ev)<0)
 76     {
 77         perror("epoll_ctl");
 78         return -2;
 79     }   
 80     struct epoll_event ev_out[_MAX_FD_SIZE_];
 81     int max=_MAX_FD_SIZE_;
 82 
 83     int timeout=5000;
 84     int i=0;
 85     int num=-1;
 86     while(1)
 87     {
 88         switch(num=epoll_wait(epoll_fd,ev_out,max,timeout))  
 89         {
 90             case -1://errno
 91                 perror("epoll_wait");
 92                 break;
 93             case 0://timeout
 94                 printf("timeout...\n");
 95                 break;
 96             default://data ready
 97                 {
 98                     for(i=0;i<num;++i)
 99                     {
100                         //get a new connect
101                         if(ev_out[i].data.fd==sock&&ev_out[i].events & EPOLL    IN)
102                         {
103                             struct sockaddr_in client;
104                             socklen_t len=sizeof(client);
105 
106                             int fd=ev_out[i].data.fd;
107                             int new_sock=accept(fd,(struct sockaddr*)&client    ,&len);
108                             if(new_sock<0)
109                             {                                                        110                                 perror("accept");
111                                 printf("%s:%d\n",strerror(errno),new_sock);
112                                 continue;
113                             }
114                             ev.events=EPOLLIN;
115                             ev.data.fd=new_sock;
116                             epoll_ctl(epoll_fd,EPOLL_CTL_ADD,new_sock,&ev);
117                             printf("get a new connect...\n");
118                         }
119                         else if(ev_out[i].data.fd>0&&ev_out[i].events & EPOL    LIN)
120                         {
121                             int fd=ev_out[i].data.fd;
122                             data_buf_p mem=(data_buf_p)malloc(sizeof(data_bu    f_t));
123                             if(mem==NULL)
124                             {
125                                 continue;   
126                             }
127                             mem->fd=fd;                                     
128                             int _s=read(mem->fd,mem->buf,sizeof(mem->buf));          129                             if(_s<0)
130                             {
131                                 perror("read");
132                                 close(fd);
133                                 free(mem);
134                             }
135                             else if(_s==0)
136                             {
137                                 printf("client close...\n");
138                                 epoll_ctl(epoll_fd,EPOLL_CTL_DEL,fd,NULL);
139                                 close(fd);
140                                 free(mem);
141                             }
142                             else if(_s>0)
143                             {
144                                 mem->buf[_s]=‘\0‘;
145                                 printf("client say:%s\n",mem->buf);
146                                 ev.events=EPOLLOUT;
147                                 ev.data.ptr=mem;
148                                 epoll_ctl(epoll_fd,EPOLL_CTL_MOD,mem->fd,&ev    );   149                             }
150                             else
151                             {
152                                 continue;
153                             }
154                         }
155             
156                         else if(ev_out[i].data.fd>0&&ev_out[i].events & EPOL    LOUT)
157                         {
158                             data_buf_p mem=(data_buf_p)ev_out[i].data.ptr;
159                             ssize_t _s=write(mem->fd,mem->buf,sizeof(mem->bu    f));
160                             close(mem->fd);
161                             epoll_ctl(epoll_fd,EPOLL_CTL_DEL,mem->fd,NULL);
162                         }                                                            
173                             else
174                             {}       
175                     }
176                 break;
177                 }
178         }
179     }
180 }
181 
182 int main(int argc,char *argv[])
183 {
184     if(argc!=3)
185     {
186         Usage(argv[0]);
187         exit(1);
188     }
189     char *ip=argv[1];
190     int port=atoi(argv[2]);
191     int listen_sock=startup(ip,port);
192     server_epoll(listen_sock);
193     close(listen_sock);
194     return 0;
195 }

运行结果:

    用telnet测试:

技术分享

技术分享

    用浏览器测试,则是:

技术分享

    若修改代码为:

164                             else if(ev_out[i].data.fd>0&&ev_out[i].events &     EPOLLOUT)
165                             {
166                                 
167                                 char *msg="HTTP/1.0 200 OK\r\n\r\nhello worl    d\r\n";
168                                 data_buf_p mem=(data_buf_p)ev_out[i].data.pt    r;
169                                 ssize_t _s=write(mem->fd,msg,strlen(msg));
170                                 close(mem->fd);
171                                 epoll_ctl(epoll_fd,EPOLL_CTL_DEL,mem->fd,NUL    L);
172                             }

    则结果为:   技术分享(2)Edge Triggered工作模式

    如果将文件句柄添加到epoll描述符的时候使用了EPOLLET标志,那么在调用epoll_wait(2)之后就有可能会被挂起,因为剩余的数据还存在于文件的输入缓冲区中,而且数据发出端还在等待一个针对已经发出的数据的反馈信息。只有在监视的文件句柄上发生了某个事件的时候ET工作模式才会汇报事件。因此在epoll_wait(2)的时候,调用者可能会放弃等待仍存在于文件输入缓冲区内的剩余数据。因此最好以下面的方式调用ET模式:

    a、基于非阻塞文件句柄

    b、只有当read(2)或write(2)返回EAGAIN时才需要挂起,等待。但这并不是说每次read()时都需要循环读,直到读到一个EAGAIN时才认为此次时间处理完成。当read()返回的读到的数据长度小于请求的数据长度时,就可以确定此时缓冲区中没有数据了。也就可以认为此事件已处理完成。

代码描述:

  1 #include<stdio.h>                                                           
  2 #include<stdlib.h>
  3 #include<sys/epoll.h>
  4 #include<sys/types.h>
  5 #include<sys/socket.h>
  6 #include<netinet/in.h>
  7 #include<arpa/inet.h>
  8 #include<unistd.h>
  9 #include<fcntl.h>
 10 #include<assert.h>
 11 #include<errno.h>
 12 #include<string.h>
 13 
 14 #define _BACKLOG_ 5
 15 #define _SIZE_ 256
 16 #define _MAX_FD_SIZE_ 64
 17 #define _BUF_SIZE_ 10240
 18 
 19 typedef struct data_buf
 20 {
 21     int fd;
 22     char buf[_BUF_SIZE_];
 23 }data_buf_t,*data_buf_p;
 24 
 25 static int set_non_block(int fd)
 26 {
 27     int old_fl=fcntl(fd,F_GETFL);
 28     if(old_fl<0)
 29     {
 30         perror("fcntl");
 31         return -1;
 32     }
 33     if(fcntl(fd,F_SETFL,old_fl|O_NONBLOCK))
 34     {
 35         perror("fcntl");
 36         return -2;
 37     }
 38     return 0;
 39 }
 40 
 41 static void Usage(char* const proc)
 42 {
 43     assert(proc);
 44     printf("%s [ip][port]\n",proc);
 45 }
 46     
 47 static int startup(char *ip,int port)
 48 {
 49     assert(ip);
 50     int sock=socket(AF_INET,SOCK_STREAM,0);
 51     if(sock<0)
 52     {
 53         perror("socket");
 54         exit(1);
 55     }
 56     
 57     int opt=1;
 58     setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
 59 
 60     struct sockaddr_in local;
 61     local.sin_family=AF_INET;
 62     local.sin_port=htons(port);
 63     local.sin_addr.s_addr=inet_addr(ip);
 64 
 65     if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
 66     {
 67         perror("bind");
 68         exit(2);   
 69     }
 70     if(listen(sock,_BACKLOG_)<0)
 71     {
 72         perror("listen");
 73         exit(3);
 74     }
 75     return sock;
 76 }
 77 
 78 
 79 int read_data(int fd,char *buf,int size)
 80 {
 81     assert(buf);
 82     memset(buf,‘\0‘,size);
 83     int index=0;
 84     ssize_t _s=-1;
 85     while(_s=read(fd,buf+index,size-index)<size)
 86     {
 87         if(errno==EAGAIN)
 88         {
 89             break;
 90         }
 91         index+=_s;     
 92     }
 93     return index;
 94 }
 95 
 96 int write_data(int fd,char *buf,int size)
 97 {
 98     int index=0;
 99     ssize_t _s=-1;
100     while(_s=write(fd,buf+index,size-index)<size)
101     {
102         if(errno==EAGAIN)
103         {
104             break;
105         }
106         index+=_s;
107     }
108     return index;
109 }
110 
111 //sock=listen_sock
112 static int server_epoll(int sock)
113 {   
114     int epoll_fd=epoll_create(_SIZE_);
115     if(epoll_fd<0)
116     {
117         perror("epoll_create");
118         return -1;
119     }
120 
121     struct epoll_event ev;
122     ev.data.fd=sock;
123     ev.events=EPOLLIN|EPOLLET;
124     if(epoll_ctl(epoll_fd,EPOLL_CTL_ADD,sock,&ev)<0)
125     {
126         perror("epoll_ctl");
127         return -2;
128     }   
129     struct epoll_event ev_out[_MAX_FD_SIZE_];
130     int max=_MAX_FD_SIZE_;
131 
132     int timeout=5000;
133     int i=0;
134     int num=-1;
135     int done=0;
136     while(!done)    
137     {
138         switch(num=epoll_wait(epoll_fd,ev_out,max,timeout))
139         {
140             case -1://errno
141                 perror("epoll_wait");
142                 break;
143             case 0://timeout
144                 printf("timeout...\n");
145                 break;
146             default://data ready
147                 {
148                     for(i=0;i<num;++i)
149                     {
150                         //get a new connect
151                         if(ev_out[i].data.fd==sock&&(ev_out[i].events&EPOLLI    N))
152                         {
153                             struct sockaddr_in client;
154                             socklen_t len=sizeof(client);
155 
156                             int fd=ev_out[i].data.fd;
157                             int new_sock=accept(fd,(struct sockaddr*)&client    ,&len);                                                                                       158                             if(new_sock<0)
159                             {
160                                 perror("accept");
161                                 printf("%s:%d\n",strerror(errno),new_sock);
162                                 continue;
163                             }
164                             set_non_block(new_sock);
165                             ev.events=EPOLLIN|EPOLLET;
166                             ev.data.fd=new_sock;
167                             epoll_ctl(epoll_fd,EPOLL_CTL_ADD,new_sock,&ev);
168                             printf("get a new connect...\n");
169                         }
170                         else
171                         {
172                             //read events ready
173                             if(ev_out[i].events&EPOLLIN)
174                             {
175                                 int fd=ev_out[i].data.fd;
176                                 data_buf_p mem=(data_buf_p)malloc(sizeof(dat    a_buf_t));
177                                 if(mem==NULL)
178                                 {                                           
179                                     perror("malloc");    
180                                     continue;
181                                 }
182                                 //read data all done...
183                                 mem->fd=fd;
184                                 ssize_t _s=read_data(mem->fd,mem->buf,sizeof    (mem->buf));
185                                 if(_s>0)
186                                 {
187                                     (mem->buf)[_s]=‘\0‘;
188                                     ev.data.ptr=mem;
189                                     printf("client:%s\n",mem->buf);
190                                     ev.events=EPOLLOUT|EPOLLET;
191                                     ev.data.ptr=mem;
192                                     epoll_ctl(epoll_fd,EPOLL_CTL_MOD,fd,&ev)    ;
193                                 }
194                                 else if(_s==0)
195                                 {
196                                     printf("client close...\n");
197                                     epoll_ctl(epoll_fd,EPOLL_CTL_DEL,fd,NULL    );
198                                     close(fd);
199                                     free(mem);     
200                                 }
201                                 else
202                                 {
203                                     continue;
204                                 }
205                             }
206                             else if(ev_out[i].events&EPOLLOUT)
207                             {
208                                 data_buf_p mem=(data_buf_p)ev_out[i].data.pt    r;
209                                 write_data(mem->fd,mem->buf,strlen(mem->buf)    );
210                                 close(mem->fd);
211                                 epoll_ctl(epoll_fd,EPOLL_CTL_DEL,mem->fd,NUL    L);
212                                 free(mem);
213                             }
214                             else
215                             {
216                             }
217                         }
218                     }
219                 }
220                 break;
221         }   
222     }
223 }
224 
225 int main(int argc,char *argv[])
226 {
227     if(argc!=3)
228     {
229         Usage(argv[0]);
230         exit(1);
231     }
232     char *ip=argv[1];
233     int port=atoi(argv[2]);
234     int listen_sock=startup(ip,port);
235     server_epoll(listen_sock);
236     close(listen_sock);
237     return 0;
238 }
239

    运行结果:

技术分享

技术分享

        epoll首先调用epoll_create建立一个epoll对象,参数size是内核保证能够正确处理的最大句柄数,多于这个最大数时内核可不保证效果。

        epoll_ctl可以操作上面的epoll,例如,将刚建立的socket加入到epoll中让其监控,或者把epoll正在监控的某个socket句柄移出epoll,不再监控它等等。

        epoll_wait在调用时,在给定的timeout时间内,当在监控的所有句柄中有事件发生时,就返回用户态的进程。

        因此可以看出epoll优于select/poll:因为后者每次调用时都要传递你所要监控的所有socket给socket/poll系统调用,这意味着需要将用户态的socket列表copy到内核态,如果以万计的句柄每次都要copy几十几百KB的内存到内核态,非常低效。而调用epoll_wait时就相当于以往调用select/poll,但是这时却不用传递socket句柄给内核,因为内核已经在epoll_ctl中拿到了要监控的句柄列表。

     

本文出自 “zwy” 博客,请务必保留此出处http://10548195.blog.51cto.com/10538195/1828197

I/O多路转接之epoll

标签:epoll

原文地址:http://10548195.blog.51cto.com/10538195/1828197

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!