用户态协议栈,为什么要实现epoll?
因为内核的epoll是跟内核协议栈一起使用的,内核协议栈处理io后通过回调的方式来操作epoll中的就绪队列,所以用户态协议栈必须要有用户态的epoll。
用户态epoll是参考内核的epoll,在用户空间实现了epoll的功能。
epoll使用红黑树和队列,红黑树存放需要检测的节点,队列存放就绪的节点。

epoll三个函数:
int epoll_create(int size);
- 分配一个eventpoll;
- 初始化红黑树的根节点epfd。
eventpoll与epfd一一对应。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
操作红黑树,根据op对红黑树进行增删改
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
把就绪队列的数据从内核copy到用户空间。
何时将红黑树中的节点加入到就绪队列中?
网卡中有数据时,内核协议栈通过回调的方式将红黑树中的节点加入到就绪队列中。
回调的时机有哪些呢?
- client端调用connect(), 发送TCP连接请求;
这里需要注意,跟server端是否调用accept没有关系,已经测试过,即使不调用accept,TCP依然连接成功 - client端通过send发送消息;
- server端通过send发送消息;
- client端调用close,进行关闭的时候。
回调的参数有哪些? - fd,确定红黑树中的节点;
- events,EPOLLIN, EPOLLOUT等等;
- eventpoll, epoll底层数据结构,可以确定是那个epoll。可以同时存在多个epoll。

如何检测到io数据发生了变化,需要调用回调呢?
这个是netmap + TCP协议栈做的事情,会有一个while(1), 不断去检测。


epoll是不是线程安全的?
epoll是线程安全的。红黑树和就绪队列,都是加锁的;epoll_wait中,使用条件变量。
- rbtree --> mutex;
- queue --> spinlock;
- epoll_wait -->cond, mutex
ET和LT是如何实现的?会不会回调?
LT: 一直回调
ET:调用一次
一直回调怎么实现?就是上面提到的,有一个while(1),不断去检测。
来看一下epoll的代码
- int epoll_create(int size);
初始化eventpoll,rbtree,rdlist,以及epoll中使用的锁和条件变量 - int epoll_ctl(int epid, int op, int sockid, struct epoll_event *event)
根据op,EPOLL_CTL_ADD/EPOLL_CTL_DEL/EPOLL_CTL_MOD,操作红黑树。 - int epoll_wait(int epid, struct epoll_event *events, int maxevents, int timeout)
将就绪队列中的数据(fd, events)从内核空间copy到用户空间, 并将数据从就绪队列中移除。
timeout > 0, 使用 pthread_cond_timedwait()
timeout < 0, 一直阻塞,使用pthread_cond_wait() - int epoll_event_callback(struct eventpoll *ep, int sockid, uint32_t event)
根据fd,从rbtree中找到节点,并加入到就绪队列中。
网友评论