两种工作方式
LT模式:水平模式,完全靠内核驱动,类似select和poll,只要有文件描述符就绪就一直通知应用程序,若不处理,就一直通知。不适用在并发数量极多的情况下,因为所有的fds都要在它的队列中产生状态信息,而每次却只有一部分fds能返回给应用程序,队列过长且需要一次次的遍历,太过忙碌,效率不高。
ET模式:边缘触发,必须将socket设置为非阻塞,要对文件描述符实现超时控制时可以使用此模式,当有文件描述符就绪时,epoll只通知应用程序一次,然后将此描述符从监视队列中清除,若不处理,epoll也不再关注该文件描述符,直到应用程序做出处理。这样可能会造成丢包,所以应用程序应该创建一张fds表,把从epoll_wait得到的状态信息记入此表,然后遍历表,完成fds的相关处理操作。在并发数量极多的情况下有优势,因为只要epoll_wait返回fds一次,这些fds就会从队列中删除且只有当其重新变为空闲状态时才能重新加入队列,这样队列不再过长且效率变高。
struct epoll_event结构体,用于存储要监视的事件和回传要发生的事件
struct epoll_event{
__uint32_t events; //epoll要监视的事件
/* EPOLLIN:可读
EPOLLOUT:可写
EPOLLPRI:紧急数据可读
EPOLLERR:发生错误
EPOLLHUP:被挂断
EPOLLET:监听该事件的方式设为边缘触发模式
EPOLLONESHOT:只监视一次,还需监视,需再加入epoll
*/
epoll_data_t data; //存储用户数据的联合体
};
typedef union epoll_data{
void *ptr;
int fd; //用户的文件描述符
__uint32_t u32;
__uint64_t u64;
};
创建epoll,系统调用
int epoll_create(int size)
int epoll_create1(int flags)
size:告知内核要监控的文件描述符个数,有助于内核决定epoll的大小。从Linux2.6.8开始,此参数被忽略,但仍要大于0以保证向后兼容性,因为epoll数据结构会随着文件描述符的添加或删除而动态调整大小
flags:0 = epoll_creat,EPOLL_CLOEXEC = 子进程没有访问父进程的权限(fork()出的任何子进程,都不会继承其父进程和父进程所拥有的文件描述符,因为若没有设置EPOLL_CLOEXEC标志,fork()会把父进程所拥有的描述符复制给子进程,会导致多个进程同时持有同一描述符,当其中的一个或多个进程close()了某个共有的文件描述符时,会导致程序的不可用,或实现过程不在我们的预期之内)
返回值:成功 = epoll的文件描述符,失败 = -1
操作epoll
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
epfd:epoll的文件描述符
op:操作方法
EPOLL_CTL_ADD = 注册新的文件描述符和要被监视的事件(将之挂到红黑树上)
EPOLL_CTL_MOD = 修改已注册的文件描述符要被监视的事件
EPOLL_CTL_DEL = 删除/注销一个文件描述符(将之从红黑树中取下)
fd:被操作的文件描述符
event:struct epoll_event结构体,告诉内核要监视什么事
返回值:成功 = 0,失败 = -1
阻塞监视发生的事件,并记录其epoll_event结构体,系统调用