1.epoll_create()函数
函数原型:int epoll_create(int size);
功能:创建一个epoll对象,返回该对象的文件描述符,这个描述符就代表这个epoll对象。
这个epoll对象最终要用close()释放,因为文件描述符总是关闭的。
size:保证其值大于0
原理:
(1)struct eventpoll *ep = (struct eventpoll*)calloc(1, sizeof(struct eventpoll));
(2)rbr结构体成员:代表创建一个红黑树的根节点[刚开始指向空]
红黑树,用来保存 键[数字]/值[结构],能够快速的通过key,把整个value取出
(3)rdlist结构成员:代表 一个双向链表的表头指针
双向链表:从头访问/遍历每个原型。
总结:创建了一个初始化eventpoll结构对象,被系统保存,rbr成员被初始化成一个棵红黑树的根节点,rdlist成员被初始化成指向一个双向链表的根。
2.epoll_ctl()函数
函数原型:int epoll_ctl(int efpd, int op, int sockid, struct epoll_event *event);
功能:把一个socket以及这个socket相关的事件添加到这个epoll对象描述符中区,目的是通过这个epoll对象来监视这个socket[客户端的TCP连接]当有数据来往时,系统会通知我们。
epfd:epoll_create()返回的epoll对象。
op:动作添加/删除/修改,对应数字1,2,3,EPOLL_CTL_ADD,EPOLL_CTL_DEL,EPOLL_CTL_MOD
添加事件:往红黑树中添加一个节点,每个客户端连入服务器后,服务器都会产生一个socket,每个socket的值都不会重复,所以这个socket就是红黑树的key。
EPOLL_CTL_NOD修改事件:你用了EPOLL_CTL_ADD把这个节点添加到红黑树上之后,才存在修改
EPOLL_CTL_DEL删除事件:把该节点从红黑树上移除,这会导致这个socket上无法收到任何系统通知事件
sockid:表示客户端连接,accept()函数的返回值,这就是红黑树的key。
event:事件信息,这里包括的是一些事件信息:EPOLL_CTL_ADD和EPOLL_CTL_MOD都要用到这个event参数里边的事件信息。
原理:
(1)epi = (struct epitem*)calloc(1, sizeof(struct epitem)); //创建一个红黑树节点
(2)epi = RB_INSERT(_epoll_rb_socket, &ep->rbr, epi); //红黑树中添加一个节点【EPOLL_CTL_ADD】
epitem.rbn代表三个指针,左子树,右子树以及父节点
(3)epi = RB_REMOVE(_epoll_rb_socket, &ep->rbr, epi); //从红黑树中移除一个节点【EPOLL_CTL_DEL】
(4)struct epitem *epi = RB_FIND(_epoll_rb_socket, &ep->rbr, &tmp); //找到节点后修改该节点的内容 【EPOLL_CTL_MOD】
if (epi) {
epi->event.events = event->events;
epi->event.events |= EPOLLERR | EPOLLHUP;
} else {
errno = -ENOENT;
return -1;
}
总结:EPOLL_CTL_ADD添加红黑树节点,EPOLL_CTL_DEL从移除红黑树节点并释放内存,EPOLL_CTL_MOD修改红黑树中的数据(epitem即可作为红黑树的节点也可作为双向链表的节点)
3.epoll_wait()函数
函数原型int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
功能:阻塞一段时间并等待事件发生,返回事件集合,也就是获取内核的实践通知。===》遍历epoll_create()时创建的双向链表,把这个双向链表中的节点数据拷贝一份,拷贝完成的就从双向链表中移除。因为双向链表中记录的是有事件发生的socket[TCP连接]
epfd:epoll_create()返回的epoll对象
events:是内存,也是数组,长度为maxevents,表示此次epoll_wait()调用可以接收到的maxevents个已经就续的读写事件,返回的是实际发生实际的TCP连接。
timeout:阻塞等待的时长。
4.内核向双向链表中添加节点
有以下四种情况
1.客户端完成三次握手:服务器调用accept()
2.客户端关闭连接:服务器需要调用close()
3.客户端发送数据:服务器需要调用read()/recv()接收
4.当可以发送数据:服务器需要调用send()/write()
5.其它