原博客地址:https://www.cnblogs.com/lojunren/p/3856290.html
文件描述符的创建
#include <sys/epoll.h>
int epoll_create ( int size );
在epoll早期的实现中,对于监控文件描述符的组织并不是使用红黑树,而是hash表。这里的size实际上已经没有意义。
注册监控事件
#include <sys/epoll.h>
int epoll_ctl ( int epfd, int op, int fd, struct epoll_event *event );
函数说明:
- fd:要操作的文件描述符
- op:指定操作类型操作类型:
- EPOLL_CTL_ADD:往事件表中注册fd上的事件
- EPOLL_CTL_MOD:修改fd上的注册事件
- EPOLL_CTL_DEL:删除fd上的注册事件
- event:指定事件,它是epoll_event结构指针类型
epoll_event定义 :
struct epoll_event
{
__unit32_t events; // epoll事件
epoll_data_t data; // 用户数据
};
结构体说明:
- events:描述事件类型,和poll支持的事件类型基本相同(两个额外的事件:EPOLLET和EPOLLONESHOT,高效运作的关键)
- epoll_data:存储用户数据
typedef union epoll_data
{
void* ptr; //指定与fd相关的用户数据
int fd; //指定事件所从属的目标文件描述符
uint32_t u32;
uint64_t u64;
} epoll_data_t;
epoll_wait函数
类似于进程的wait()函数吧。
#include <sys/epoll.h>
int epoll_wait ( int epfd, struct epoll_event* events, int maxevents, int timeout );
函数说明:
- 返回:成功时返回就绪的文件描述符的个数,失败时返回-1并设置errno
- timeout:指定epoll的超时时间,单位是毫秒。当timeout为-1是,epoll_wait调用将永远阻塞,直到某个时间发生。当timeout为0时,epoll_wait调用将立即返回。
- maxevents:指定最多监听多少个事件
- events:检测到事件,将所有就绪的事件从内核事件表中复制到它的第二个参数events指向的数组中。
EPOLLONESHOT事件
使用场合:
一个线程在读取完某个socket上的数据后开始处理这些数据,而数据的处理过程中该socket又有新数据可读,此时另外一个线程被唤醒来读取这些新的数据。
于是,就出现了两个线程同时操作一个socket的局面。可以使用epoll的EPOLLONESHOT事件实现一个socket连接在任一时刻都被一个线程处理。
作用:
对于注册了EPOLLONESHOT事件的文件描述符,操作系统最多出发其上注册的一个可读,可写或异常事件,且只能触发一次。
使用:
注册了EPOLLONESHOT事件的socket一旦被某个线程处理完毕,该线程就应该立即重置这个socket上的EPOLLONESHOT事件,以确保这个socket下一次可读时,其EPOLLIN事件能被触发,进而让其他工作线程有机会继续处理这个sockt。
效果:
尽管一个socket在不同事件可能被不同的线程处理,但同一时刻肯定只有一个线程在为它服务,这就保证了连接的完整性,从而避免了很多可能的竞态条件。