IO多路复用
Redis为什么性能高?
redis采用了IO多路复用, IO多路复用的模型有select,poll, epoll, 默认使用的事epoll.
注:
-
先理解有用户空间(应用程序控制,比如redis,mysql等安装的软件程序),内核空间(操作系统控制)
-
如果服务器端和客户端之间有100万个链接, select , poll 这两种模型, 每次处理时候,都会将这100万个链接从用户空间传给内核空间,然后内核空间判断哪些做了处理哪些没做处理,然后内核空间再将这100万个链接返回给用户空间; 之后用户空间再遍历这100万个链接,看哪个可以处理。 而 epoll模型,是在内核空间上维护着这100万个链接。不需要用户空间和内核空间来回传递这100万个链接, epoll模型有三个系统调用完成, create/epoll_ctl/epoll_wait
a. 调用epoll_create()建立一个epoll对象(在epoll文件系统中为这个句柄对象分配资源)
b. 调用epoll_ctl向epoll对象中添加这100万个连接的套接字
c. 调用epoll_wait收集发生的事件的连接
总结:
1. 每次调用select/poll,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
2. 同时每次调用select/poll都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
3. 针对select支持的文件描述符数量太小了,默认是1024
4. select返回的是含有整个句柄的数组,应用程序需要遍历整个数组才能发现哪些句柄发生了事件;
5. select的触发方式是水平触发,应用程序如果没有完成对一个已经就绪的文件描述符进行IO操作,那么之后每次
select调用还是会将这些文件描述符通知进程。
相比select模型,poll使用链表保存文件描述符,因此没有了监视文件数量的限制,但其他三个缺点依然存在。
epoll 优点:
1. epoll 没有最大并发连接的限制,上限是最大可以打开文件的数目,这个数字一般远大于 2048, **一般来说这
个数目和系统内存关系很大** ,具体数目可以 cat /proc/sys/fs/file-max 察看。
2. 效率提升, epoll 最大的优点就在于它**只管你“活跃”的连接** ,而跟连接总数无关,因此在实际的网络环境
中, epoll 的效率就会远远高于 select 和 poll 。
3. 内存拷贝, epoll 在这点上使用了“共享内存”,这个内存拷贝也省略了。
redis epoll 实现思路:
调用 epoll_create方法, 创建一个数据结构 eventpoll对象,以及一个双向链表(rdlist),rdlist中只存放可以处理的事件
eventpoll对象是红黑树结构, 当调用event_ctl 命令时,将信息放入 eventpoll对象。一旦事件发生,会调用ep_poll_callback方法将事件句柄添加到双向链表rdlist中。
双向链表rdlist中存储事件发生的信息(其中包含事件句柄), 当调用epoll_await方法时候,返回rdlist链表。
优势:
- 不用重复传递。我们调用epoll_wait时就相当于以往调用select/poll,但是这时却不用传递socket句柄给
内核,因为内核已经在epoll_ctl中拿到了要监控的句柄列表。
- 在内核里,一切皆文件。所以,epoll向内核注册了一个文件系统,用于存储上述的被监控socket。当你调用
epoll_create时,就会在这个虚拟的epoll文件系统里创建一个file结点。当然这个file不是普通文件,它只
服务于epoll。
epoll在被内核初始化时(操作系统启动),同时会开辟出epoll自己的内核高速cache区,用于安置每一个我
们想监控的socket,这些socket会以红黑树的形式保存在内核cache里,以支持快速的查找、插入、删除。这
个内核高速cache区,就是建立连续的物理内存页,然后在之上建立slab层,简单的说,就是物理上分配好你
想要的size的内存对象,每次使用时都是使用空闲的已分配好的对象。
- 极其高效的原因:
这是由于我们在调用epoll_create时,内核除了帮我们在epoll文件系统里建了个file结点,在内核cache里
建了个红黑树用于存储以后epoll_ctl传来的socket外,还会再建立一个list链表,用于存储准备就绪的事
件,当epoll_wait调用时,仅仅观察这个list链表里有没有数据即可。有数据就返回,没有数据就sleep,等
到timeout时间到后即使链表没数据也返回。所以,epoll_wait非常高效。
FRI
如此,一颗红黑树,一张准备就绪句柄链表,少量的内核cache,就帮我们解决了大并发下的socket处理问题。执
行epoll_create时,创建了红黑树和就绪链表,执行epoll_ctl时,如果增加
socket句柄,则检查在红黑树中是否存在,存在立即返回,不存在则添加到树干
上,然后向内核注册回调函数,用于当中断事件来临时向准备就绪链表中插入数
据。执行epoll_wait时立刻返回准备就绪链表里的数据即可。