in the above mentioned if take out from 100 different places, then epoll equivalent to a mobile phone, when the takeaway arrives, the delivery agent can inform you, so as to achieve each to get, less go a lot of road.
How does it achieve these effects?
Features of the Epoll
epoll是select/poll的强化版,同是多路复用的函数,epoll有了很大的改进。
- Supports listening for large number of socket descriptors *
Within a process, select can open the FD is limited, by the macro fd_setsize set, the default value is 1024. At some point, this value is far from enough. There are two solutions, one is to modify the macro and then recompile the kernel, but at the same time the network efficiency will be reduced, and the second is to use multi-process to solve, but the creation of a number of processes is a cost, and inter-process data synchronization is not more convenient between the threads.
And Epoll does not have this limit, it supports the maximum FD limit is far greater than 1024, in 1GB memory of the machine is about 100,000 (the specific number can be Cat/proc/sys/fs/file-max view);
- Increase in efficiency
The SELECT function returns every time a listener's socket has an event, but does not filter out the socket that has the event, but changes its flag amount in the socket, so each time it hears the event, it needs to iterate through the socket group. The complexity of Time is O (n). When the number of FD increases, the efficiency decreases linearly.
And Epoll, each time the socket that generates the event is added to a list, and then we can directly manipulate the list, and the socket that does not generate the event is filtered out, greatly improving the IO efficiency. This is especially noticeable when the number of sockets listening is large and the number of active is very small.
The use of Epoll
epoll的使用主要在于三个函数。
1. epoll_create (int size);
创建一个epoll的句柄,size用来告诉内核这个监听的数目最大值。注意!是数量的最大值,不是fd的最大值,切勿搞混。当创建好epoll句柄后,它就是会占用一个fd值,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。
2. int epoll_ctl (int epfd, int op, int fd, struct epoll_event *event);
epoll的事件注册函数。epfd是epoll的句柄,即epoll_create的返回值;op表示动作:用三个宏表示:EPOLL_CTL_ADD:注册新的fd到epfd中;EPOLL_CTL_MOD:修改已经注册的fd的监听事件;EPOLL_CTL_DEL:从epfd中删除一个fd;fd是需要监听的套接字描述符;event是设定监听事件的结构体,数据结构如下:
typedefunion 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 */};
events可以是以下几个宏的集合:EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);EPOLLOUT:表示对应的文件描述符可以写;EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);EPOLLERR:表示对应的文件描述符发生错误;EPOLLHUP:表示对应的文件描述符被挂断;EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,就会把这个fd从epoll的队列中删除。如果还需要继续监听这个socket的话,需要再次把这个fd加入到EPOLL队列里
3. int epoll_wait (int epfd, struct epoll_event *events, int maxevents, int timeout);
等待事件的产生,返回需要处理的事件的数量,并将需处理事件的套接字集合于参数events内,可以遍历events来处理事件。参数epfd为epoll句柄events为事件集合参数timeout是超时时间(毫秒,0会立即返回,-1是永久阻塞)。该函数返回需要处理的事件数目,如返回0表示已超时。
function Use small example
#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>#include <errno.h>#include <stdlib.h>#include <string.h>#define MAXLINE //Maximum length #define OPEN_MAX#define LISTENQ#define Serv_port 8000#define INFTIM#define IP_ADDR "10.73.219.151"intMain () {structEpoll_event EV, events[ -];structSockaddr_in clientaddr, serveraddr;intEPFD;intLISTENFD;//Monitor FD intMaxiintNfds;intIintSOCK_FD, CONN_FD;CharBuf[maxline]; EPFD = Epoll_create ( the);//Generate Epoll handleLISTENFD = socket (af_inet, Sock_stream,0);//Create socketsEV.DATA.FD = LISTENFD;//Set the file descriptor associated with the event to be processedEv.events = Epollin;//Set the type of event to be processedEpoll_ctl (EPFD, Epoll_ctl_add, LISTENFD, &ev);//Register Epoll event memset(&SERVERADDR,0,sizeof(SERVERADDR)); serveraddr.sin_family = af_inet; SERVERADDR.SIN_ADDR.S_ADDR = htonl (Inaddr_any); Serveraddr.sin_port = htons (Serv_port); Bind (LISTENFD, (structsockaddr*) &serveraddr,sizeof(SERVERADDR));//Binding Socket InterfaceSocklen_t Clilen; Listen (LISTENFD, Listenq);//Switch to listener socket intN while(1) {Nfds = Epoll_wait (epfd,events, -, -);//Wait for event to occur //Handle all events that occurred for(i=0; i<nfds;i++) {if(EVENTS[I].DATA.FD = = LISTENFD)//have a new connection{Clilen =sizeof(structSOCKADDR_IN); CONN_FD = Accept (LISTENFD, (structsockaddr*) &clientaddr, &clilen);printf("Accept a new client:%s\n", Inet_ntoa (CLIENTADDR.SIN_ADDR)); EV.DATA.FD = CONN_FD; Ev.events = Epollin;//Set listener events to be writableEpoll_ctl (EPFD, Epoll_ctl_add, CONN_FD, &ev);//New sockets}Else if(Events[i].events & Epollin)//Readable events{if((sock_fd = EVENTS[I].DATA.FD) <0)Continue;if(n = recv (sock_fd, buf, MAXLINE,0)) <0) {if(errno = = Econnreset) {Close (SOCK_FD); EVENTS[I].DATA.FD =-1; }Else{printf("ReadLine error\n"); } }Else if(n = =0) {Close (SOCK_FD);printf("off \ n"); EVENTS[I].DATA.FD =-1; }printf("%d-->%s\n", SOCK_FD, BUF); EV.DATA.FD = SOCK_FD; Ev.events = Epollout; Epoll_ctl (Epfd,epoll_ctl_mod,sock_fd,&ev);//Modify listener events to be readable}Else if(Events[i].events & Epollout)//Writable Events{sock_fd = EVENTS[I].DATA.FD;printf("out\n");scanf('%s ', buf); Send (SOCK_FD, buf, MAXLINE,0); EV.DATA.FD = SOCK_FD; Ev.events = Epollin; Epoll_ctl (EPFD, EPOLL_CTL_MOD,SOCK_FD, &ev); } } }return 0; }
Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.
The Epoll function of LINUX-C network programming