IO 多路复用
详细见: link.
redis默认使用epoll多路复用函数库。
IO多路复用理解:每一个网络连接都对应一个文件描述符(fd),高效的网络服务器都可以供多个客户端同时连接即产生多个文件描述符,然后单线程不断的去轮询这些描述符,如果有消息就处理。大致代码如下:
while(true){
for(fdx in fdA -fdE){
if(fdx有数据)
读取fdx,处理数据;
}
}
select IO多路复用
(1)首先将需要被监听的文件描述符放入数组中—fds[],数组中的每一个元素是一个数字,代表一个文件描述符的编号
(2)将fds数组存的元素,放到一个rset中去,rset是bitmap类型。该bitmap只有1024位,也就是说只能表示1024个描述符。
(3)执行select(max+1, &rset, NULL, NULL ,NULL)函数,第一个参数max表示监听的文件描述符最大值,第二个参数表示读文件描述符集合,即bitmap标注了哪些文件描述符被监听,第三个是写文件描述符集合,第四个是异常描述符;最后一个是超时时间。select需要做的工作如下:
(a)将bitmap从用户态拷贝到内核态
(b)内核监听fd上的数据,如果有数据则标记bitmap对应的位,然后select函数返回。这是一个循环,如果一直没数据来,则内核一直在判断,这是阻塞式的。
(c)回到用户态,遍历fds集合,判断哪些fd被置位了即有数据来了,然后读取fd上的数据并处理。
select缺点:
(1)bitmap默认为1024,即最多有1024个连接。
(2)rset即bitmap不可重用,每次都要置为0
(3)每次都要把rset从用户态拷贝到核心态
(4)用户态需要遍历fds哪些fd有数据.
poll IO多路复用
poll工作流程总体和select一样。但是poll没有用bitmap来标记需要标记需要监听的fd,而是采用的pollfd数组。pollfd结构体中有一个字段为revents,当内核监听到对应fd有数据时,将revents字段置为1(原来为0),这样当之后在用户态遍历这个数组的时,只需要将其置0就可以达到重用,而不用再像bitmap那样得把每个位置上都置为0.
epoll IO多路复用
当某一进程调用epoll_create方法时,Linux内核会创建一个eventpoll结构体,这个结构体中有两个成员与epoll的使用方式密切相关。 每一个epoll对象都有一个独立的eventpoll结构体,用于存放通过epoll_ctl方法向epoll对象中添加进来的事件。这些事件都会挂载在红黑树中,如此,重复添加的事件就可以通过红黑树而高效的识别出来(红黑树的插入时间效率是lgn,其中n为树的高度)。eventpoll结构体如下所示:
在epoll中,对于每一个事件,都会建立一个epitem结构体,如下所示:
当调用epoll_wait检查是否有事件发生时,只需要检查eventpoll对象中的rdlist双链表中是否有epitem元素即可。如果rdlist不为空,则把发生的事件复制到用户态,同时将事件数量返回给用户。