epoll
1. 在内核创建eventpoll结构体,返回句柄epfd(唯一标识)
eventpoll包含存放被监听的fd的红黑树,和存放已就绪的fd的链表
2. 将要监听的fd加入到epoll红黑树中,并设置callback回调函数
callback触发时,就将红黑树的fd加入到就绪链表中
3. 检查就绪链表是否为空,如果没有就绪的fd,则sleep等待就绪元素进入
4. 如果不为空,返回就绪fd的数量,将内核空间就绪队列的元素拷贝到用户空间的events中
- select fd数量有限
- poll虽然无限,但用链表存储,监听fd过多性能不好
- epoll使用红黑树,遍历效率高
LevelTriggered
- fd有数据可读,会重复通知多次,直至数据被读完
- epoll_wait队列中就绪的fd复制给用户空间后不会被立刻删除,如果数据没有被读完会被重新添加回队列
EdgeTriggered
- 当有数据可读时只会通知一次
- epoll_wait队列中就绪的fd复制给用户空间后直接移除
工作流程
单线程网络模型流程:
1. serversocket fd注册到epoll实例,然后给他绑定一个处理器(tcpAccepthandler)
2. 在等待就绪之前调用beforesleep,然后进去等待就绪
就绪分为两种情况:
- serverSocket有可读事件(客户端连接上serverSocket),然后调用serversocket的处理器,将客户端socket fd 注册到epoll上
- 客户端socket可读,调用readQueryFromClient处理器,读取请求数据并且写出响应
a. readQueryFromClient处理器逻辑
b. 给每一个客户端封装一个client结构体,里面有queryBuf,将读取的数据写入queryBuf,然后将其数据转换为Redis命令(封装到argv数组)
c. 命令用Dict封装,根据命令名找到对应的command,执行command得到响应结果,然后把结果写出返回给client
d. client存储着buf数组和reply链表接受相应结果,buf数组不够就放到reply链表上
e. 然后将客户端添加到server.clients_pending_write队列,等待被写出
全局图