联合体epoll_data用来保存触发事件的某个文件描述符相关的数据。例如一个client连接到服务器,服务器通过调用accept函数可以得到于这个client对应的socket文件描述符,可以把这文件描述符赋给epoll_data的fd字段,以便后面的读写操作在这个文件描述符上进行。
(2)epoll_create
函数声明:intepoll_create(intsize)
函数说明:该函数生成一个epoll专用的文件描述符,其中的参数是指定生成描述符的最大范围。
(3) epoll_ctl函数
函数声明:intepoll_ctl(int epfd,int op, int fd, struct epoll_event *event)
函数说明:该函数用于控制某个文件描述符上的事件,可以注册事件、修改事件、删除事件。
epfd:由 epoll_create 生成的epoll专用的文件描述符;
op:要进行的操作,可能的取值EPOLL_CTL_ADD 注册、EPOLL_CTL_MOD 修改、EPOLL_CTL_DEL 删除;
fd:关联的文件描述符;
event:指向epoll_event的指针;
如果调用成功则返回0,不成功则返回-1。
(4) epoll_wait函数
函数声明:int epoll_wait(int epfd, structepoll_event * events, int maxevents, int timeout)
函数说明:该函数用于轮询I/O事件的发生。
epfd:由epoll_create 生成的epoll专用的文件描述符;
epoll_event:用于回传代处理事件的数组;
maxevents:每次能处理的事件数;
timeout:等待I/O事件发生的超时值;
返回发生事件数。
7 设计思路及模板
首先通过create_epoll(int maxfds)来创建一个epoll的句柄,其中maxfds为你的epoll所支持的最大句柄数。这个函数会返回一个新的epoll句柄,之后的所有操作都将通过这个句柄来进行操作。在用完之后,记得用close()来关闭这个创建出来的epoll句柄。
然后在你的网络主循环里面,调用epoll_wait(int epfd, epoll_event events, int max_events,int timeout)来查询所有的网络接口,看哪一个可以读,哪一个可以写。基本的语法为:
nfds = epoll_wait(kdpfd, events, maxevents, -1);
其中kdpfd为用epoll_create创建之后的句柄,events是一个epoll_event*的指针,当epoll_wait函数操作成功之后,events里面将储存所有的读写事件。max_events是当前需要监听的所有socket句柄数。最后一个timeout参数指示 epoll_wait的超时条件,为0时表示马上返回;为-1时表示函数会一直等下去直到有事件返回;为任意正整数时表示等这么长的时间,如果一直没有事件,则会返回。一般情况下如果网络主循环是单线程的话,可以用-1来等待,这样可以保证一些效率,如果是和主循环在同一个线程的话,则可以用0来保证主循环的效率。epoll_wait返回之后,应该进入一个循环,以便遍历所有的事件。
对epoll 的操作就这么简单,总共不过4个API:epoll_create, epoll_ctl,epoll_wait和close。以下是man中的一个例子。
struct epoll_event ev, *events;
for(;;)
{
nfds = epoll_wait(kdpfd, events, maxevents, -1); //等待IO事件
for(n = 0; n < nfds; ++n)
{
//如果是主socket的事件,则表示有新连接进入,需要进行新连接的处理。
if(events[n].data.fd == listener)
{
client = accept(listener, (struct sockaddr *) &local, &addrlen);
if(client < 0)
{
perror("accept error");
continue;
}
// 将新连接置于非阻塞模式
setnonblocking(client);
ev.events = EPOLLIN | EPOLLET;
//注意这里的参数EPOLLIN | EPOLLET并没有设置对写socket的监听,
//如果有写操作的话,这个时候epoll是不会返回事件的,
//如果要对写操作也监听的话,应该是EPOLLIN | EPOLLOUT | EPOLLET。
// 并且将新连接也加入EPOLL的监听队列
ev.data.fd = client;
// 设置好event之后,将这个新的event通过epoll_ctl
if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0)
{
//加入到epoll的监听队列里,这里用EPOLL_CTL_ADD
//来加一个新的 epoll事件。可以通过EPOLL_CTL_DEL来减少
//一个epoll事件,通过EPOLL_CTL_MOD来改变一个事件的监听方式。
fprintf(stderr, "epoll set insertion error: fd=%d"0, client);
return -1;
}
}
else
// 如果不是主socket的事件的话,则代表这是一个用户的socket的事件,
// 则用来处理这个用户的socket的事情是,比如说read(fd,xxx)之类,或者一些其他的处理。
do_use_fd(events[n].data.fd);
}
}8 EPOLL模型的简单实例
#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