1.思考
在tcp的编程接口socket,bind,connect,listen,accpect,recv,send,close这些中,
哪些动作对方能感知到可以产生回调?
connect,send,close,recv
2.何时回调epoll模块
1.三次握手成功时,会有一次回调;
2.接受到客户端发来数据的时候,且要返回ACK给客户端的时候,会有一次回调;
3.服务器端给客户端发消息之后服务器端收到来自客户端的ACK消息,服务器端会把服务器端的send_buf
的空间里面的客户端已经确认收到的消息对应的空间清空,这里也会产生回调;
4.当客户端向服务器端发送close请求的时候,服务器端也会产生回调.
协议栈返回fd,status和epoll_fd(红黑树的代号)就可以解决协议栈与epoll之间的关系.

3.为什么要自己做一个用户态的epoll?
在操作系统的epoll是被内核的协议栈调用的,我们如果想要对它有特点的需求,需要自己实现一个.
4.epoll是怎么实现的?
理解的关键就是红黑树与就绪队列,红黑树里的结点和就绪队列里的结点是一样的.
红黑树里存储的是所有需要监控的fd,就绪队列里面存储的是所有待处理的fd.
红黑树里的小结点的内容包含了fd和event.
epoll_create(1);--创建红黑树
中的参数最早的时候是要创建这个长度的就绪队列有多长,代表一次最多检测多少个,
之后把这个队列变成链式存储了,这个长度自然也就没有必要了.也就是说之前epoll中对应的就绪队
列是顺序存储的队列,现在的就绪队列改成是链式存储的队列了.
为了兼容之前的版本,我们现在也加上了这个参数,哪个版本改的呢?应该是3.0版本以后改的,可以
查实一下.
经查实,这个说法是错误的,内核在2.6.9版本中更改了eventpoll的存储结构,将其由原来的的hash
表存储改成现在的红黑树的存储,原来的那个size的存储是哈希表的长度.
epoll_ctl(epfd,op,fd,ev);--增删改查红黑树上的结点
1.epfd-->eventpoll;
2.
epoll_wait(epfd,events,length,time)循环loop,把就绪队列里面的数据copy出来(select是拷贝就
绪的).
time的作用是预防就绪队列为空,等待time时间不为空.
0 ---不阻塞
-1---一直阻塞
就绪队列是怎么管理的?如何进行增删改查的?
通过回调函数将对绪队列中的结点做相关操作,epoll_wait则去将队列里面的结点拷贝到红黑树中,
回调和epoll是两个流程,回调是协议栈实现的,回调函数操作的就绪队列.
epoll快是因为拷贝的是就绪队列,但select拷贝的是全部队列。
有个问题:
拷贝的队列用的内存是哪里的内存?用户空间还是内核空间的?
2.6.1版本
struct eventpoll {
/* Protect the this structure access */
rwlock_t lock;
/*
* This semaphore is used to ensure that files are not removed
* while epoll is using them. This is read-held during the event
* collection loop and it is write-held during the file cleanup
* path, the epoll file exit code and the ctl operations.
*/
struct rw_semaphore sem;
/* Wait queue used by sys_epoll_wait() */
wait_queue_head_t wq;
/* Wait queue used by file->poll() */
wait_queue_head_t poll_wait;
/* List of ready file descriptors */
struct list_head rdllist;
/* Size of the hash */
unsigned int hashbits;
/* Pages for the "struct epitem" hash */
char *hpages[EP_MAX_HPAGES];
};
在2.6.9版本中做了修改
2.6.11.10版本
struct eventpoll {
/* Protect the this structure access */
rwlock_t lock;
/*
* This semaphore is used to ensure that files are not removed
* while epoll is using them. This is read-held during the event
* collection loop and it is write-held during the file cleanup
* path, the epoll file exit code and the ctl operations.
*/
struct rw_semaphore sem;
/* Wait queue used by sys_epoll_wait() */
wait_queue_head_t wq;
/* Wait queue used by file->poll() */
wait_queue_head_t poll_wait;
/* List of ready file descriptors */
struct list_head rdllist;
/* RB-Tree root used to store monitored fd structs */
struct rb_root rbr;
};
5. et和lt如何实现的?
et(边沿触发)模式下:
触发完一次后,设置一个flag,即使recvbuf不为空,也不会被触发;
lt(水平触发)模式下:
触发完一次后,如果recvbuf不为空,就会一直触发;
et和lt是通过回调函数次数实现的:
if(触发过一次)
{
if(buf不为空 && lt模式 )
{
继续触发;
}
}
如何做到一直回调?只能在回调的时候判断.
当send的时候,
et模式下,如果sendbuf中有剩余空间只触发一次,即使后面有剩余空间,也不会触发.
lt模式下,如果sendbuf中有剩余空间,就会一直触发.
epoll 对文件的描述符的操作有两种模式 : LT(Level Trigger, 电平触发)模式 和 ET(Edge
Trigger ,边沿触发)模式。LT模式是默认的工作模式,这个模式下epoll相当于一个效率较高的poll。
当往epoll中内核事件表中注册EPOLLET事件时,epoll将以ET模式来操作该文件描述符。ET是epoll的
高效模式。
对于采用LT工作的文件描述符,当epolll_wait检测到其上有事件发生并将此事件通知应用程
序后,应用程序可以不立即处理该事件。这样当应用程序下次调用epoll_wait时,epolll_wait还会再
次向应用程序通知此事件,直到有该事件被处理。而对于采用ET模式的文件描述符,当epoll_wait检
测当其上有事件发生时并将此事件通知应用程序后,应用程序必须立即处理该事件,因为后序的
epolll_wait调用不再讲此事件通知应用程序,可见,ET模式在很大程度上降底了同一个epoll事件被
重复触发的次数,因此效率要比LT模式高。
6.epoll的线程安全设计
1.对rbtree-->加锁;
2.对queue -->spinlock
3.epoll_wati,cond,mutex
7.开源项目中的ET和LT的选择
ET模式
因为ET模式只有从unavailable到available才会触发,所以
读事件:需要使用while循环读取完,一般是读到EAGAIN,也可以读到返回值小于缓冲区大小;
如果应用层读缓冲区满:那就需要应用层自行标记,解决OS不再通知可读的问题
写事件:需要使用while循环写到EAGAIN,也可以写到返回值小于缓冲区大小
如果应用层写缓冲区空(无内容可写):那就需要应用层自行标记,解决OS不再通知可写的问题。
LT模式
因为LT模式只要available就会触发,所以:
读事件:因为一般应用层的逻辑是“来了就能读”,所以一般没有问题,无需while循环读取到EAGAIN;
如果应用层读缓冲区满:就会经常触发,解决方式如下;
写事件:如果没有内容要写,就会经常触发,解决方式如下。
LT经常触发读写事件的解决办法:修改fd的注册事件,或者把fd移出epollfd。
总结
目前好像还是LT方式应用较多,包括redis,libuv.
LT模式的优点在于:事件循环处理比较简单,无需关注应用层是否有缓冲或缓冲区是否满,只管上报事件
缺点是:可能经常上报,可能影响性能。
在我目前阅读的开源项目中:
TARS用的是ET,Redis用的是LT,Nginx可配置(开启NGX_HAVE_CLEAR_EVENT--ET,开启NGX_USE_LEVEL_EVENT则为LT).
8.ET和LT的测试
menwen的ET和LT测试,搜索【LT和ET的服务端和客户端代码】

本文详细探讨了TCP编程接口中socket等操作的回调机制,epoll模块的使用时机,为何自定义用户态epoll,以及EPOLL的红黑树与就绪队列实现。重点讲解了ET和LT模式在实际应用中的区别,涉及线程安全设计和开源项目中的模式选择。
318

被折叠的 条评论
为什么被折叠?



