1. struct epoll_event结构:
结构体epoll_event被用于注册所感兴趣的事件和回传所发生待处理的事件,定义如下:
typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;//保存触发事件的某个文件描述符相关的数据
struct epoll_event {
__uint32_t events; /* epoll event */
epoll_data_t data; /* User data variable */
};
其中events表示感兴趣的事件和被触发的事件,可能的取值为:
EPOLLIN:表示对应的文件描述符可以读;
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数可读;
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: ET的epoll工作模式;
所涉及到的函数有:
3.2epoll_create()函数
//格式:int epoll_create(int size);
//功能:创建一个epoll对象,即eventpoll,返回该对象的描述符【文件描述符】,这个描述符就代表这个epoll对象,后续会用到;
//这个epoll对象最终要用close(),因为文件描述符/句柄 总是关闭的;
//size: >0;表示监听的数量
//原理:
//a)struct eventpoll ep = (struct eventpoll)calloc(1, sizeof(struct eventpoll));
//b)rbr结构成员:代表一颗红黑树的根节点[刚开始指向空],把rbr理解成红黑树的根节点的指针;
//红黑树,用来保存 键【数字】/值【结构】,能够快速的通过你给key,把整个的键/值取出来;
//c)rdlist结构成员:代表 一个双向链表的表头指针;
//双向链表:从头访问/遍历每个元素特别快;next。
//d)总结:创建了一个eventpoll结构对象,被系统保存起来;
//rbr成员被初始化成指向一颗红黑树的根【有了一个红黑树】;
//rdlist成员被初始化成指向一个双向链表的根【有了双向链表】;
//(3.3)epoll_ctl()函数
//格式:int epoll_ctl(int efpd,int op,int sockid,struct epoll_event *event);
//功能:把一个socket以及这个socket相关的事件添加到这个epoll对象描述符中去,目的就是通过这个epoll对象来监视这个socket【客户端的TCP连接】上数据的来往情况;
//当有数据来往时,系统会通知我们;
//我们把感兴趣的事件通过epoll_ctl()添加到系统,当这些事件来的时候,系统会通知我们;
//efpd:epoll_create()返回的epoll对象描述符;
//op:动作,添加/删除/修改 ,对应数字是1,2,3, ----对应宏定义:EPOLL_CTL_ADD, EPOLL_CTL_DEL ,EPOLL_CTL_MOD
//EPOLL_CTL_ADD添加事件:等于你往红黑树上添加一个节点,每个客户端连入服务器后,服务器都会产生 一个对应的socket,每个连接,这个socket值,都不重复
//所以,这个socket就是红黑树中的key,把这个节点添加到红黑树上去;
//EPOLL_CTL_MOD:修改事件;你 用了EPOLL_CTL_ADD把节点添加到红黑树上之后,才存在修改;
//EPOLL_CTL_DEL:是从红黑树上把这个节点干掉;这会导致这个socket【这个tcp链接】上无法收到任何系统通知事件;
//sockid:表示客户端连接,就是你从accept();这个是红黑树里边的key;
//event:事件信息,这里包括的是 一些事件信息;EPOLL_CTL_ADD和EPOLL_CTL_MOD都要用到这个event参数里边的事件信息;
//原理:
//a)epi = (struct epitem*)calloc(1, sizeof(struct epitem));
//b)epi = RB_INSERT(_epoll_rb_socket, &ep->rbr, epi); 【EPOLL_CTL_ADD】增加节点到红黑树中
//epitem.rbn ,代表三个指针,分别指向红黑树的左子树,右子树,父亲;
//epi = RB_REMOVE(_epoll_rb_socket, &ep->rbr, epi);【EPOLL_CTL_DEL】,从红黑树中把节点干掉
//EPOLL_CTL_MOD,找到红黑树节点,修改这个节点中的内容;
//红黑树的节点是epoll_ctl[EPOLL_CTL_ADD]往里增加的节点;面试可能考
//红黑树的节点是epoll_ctl[EPOLL_CTL_DEL]删除的;
//总结:
//EPOLL_CTL_ADD:等价于往红黑树中增加节点
//EPOLL_CTL_DEL:等价于从红黑树中删除节点
//EPOLL_CTL_MOD:等价于修改已有的红黑树的节点
//当事件发生,我们如何拿到操作系统的通知;
//(3.4)epoll_wait()函数
//格式:int epoll_wait(int epfd,struct epoll_event *events,int maxevents,int timeout);
//功能:阻塞一小段时间并等待事件发生,返回事件集合,也就是获取内核的事件通知;
//说白了就是遍历这个双向链表,把这个双向链表里边的节点数据拷贝出去,拷贝完毕的就从双向链表里移除;只是从双向链表移除,不是从内存中干掉
//因为双向链表里记录的是所有有数据/有事件的socket【TCP连接】;
//因为等了一小段时间,只有有事件的socket才会被扔到双向链表里
//参数epfd:是epoll_create()返回的epoll对象描述符;
//参数events:是内存,也是数组,长度 是maxevents,表示此次epoll_wait调用可以收集到的maxevents个已经继续【已经准备好的】的读写事件;
//说白了,就是返回的是 实际 发生事件的tcp连接数目;
//就是把双向链表里的拷贝到events
//参数timeout:阻塞等待的时长;
//因为这是io复用技术,所以epoll是卡在那里的,就是这个wait函数卡
//如果双向链表有数据,那么就不会等待时间,会立即返回
//epitem结构设计的高明之处:既能够作为红黑树中的节点,又能够作为双向链表中的节点;
//某个socket在双向链表中,那么这个socket一定发生了某个事件,只有发生了事件的tcp链接的socket才会被扔进双向链表
//(3.5)内核向双向链表增加节点
//一般有四种情况,会使操作系统把节点插入到双向链表中;
//a)客户端完成三路握手;服务器要accept();
//b)当客户端关闭连接,服务器也要调用close()关闭;回收资源
//c)客户端发送数据来的;服务器要调用read(),recv()函数来收数据;
//d)当可以发送数据时;服务武器可以调用send(),write();
//e)其他情况;写实战代码再说;