IO多路复用模型:select、poll、epoll对比

  created  by  鱼鱼 {{tag}}
创建于 2020年07月25日 17:34:30 最后修改于 2020年08月11日 00:13:39

IO多路复用

    我们平时提到的I/O几乎都是同步 阻塞模型,譬如网络请求的socket IO,在数据返回前,相应的线程或是进程将会一直 阻塞直到数据返回,比较直接的处理便是针对IO流一对一的监听,但在IO返回前,相应的系统资源会平白无故的浪费,这种处理方式会大大降低服务器的吞吐。

    如果我们用很少的线程来监听这些IO,就能实现对系统资源的更好利用,在相应的socket有数据返回时才去读取数据。这种方式被称作IO多路复用,在Linux系统中,实现IO多路复用的方式(从古老到新)有select、poll和epoll。现在很多中间件都使用epoll IO多路复用模型才因此有着很高的性能和吞吐。此处简单描述三种方式的实现和区别。


select

    select可由一个固定的进程同时监听多个 文件描述符,当有数据就绪时,监听进程会被唤醒并轮询socket(因为并不知道是哪一个准备就绪了)。基于轮询的考量,select进程默认最多监视1024个(这一数字是可以调整的) 文件描述符,用一个定长数组维护(这个数组是从用户空间额外拷贝到内核空间的,也占用了额外的内存)。select的这种轮询方式有效的提升了并发量,但是由于轮询,其性能依旧有很大限制。

poll

    poll是select的简单优化,将记录 文件描述符的数据结构由list改为了链表,解除了1024的限制,但其需要轮询的时间复杂度和空间复杂度上与select相比没有任何优化。而且在监听的 文件描述符很多时其轮询时间复杂度会更高。

epoll

    epoll为event poll,不同于轮询,epoll采用被回调的方式进行多路复用,在 文件描述符就绪后,会callback给epoll进程,这样一来不仅没有监听 文件描述符的限制,同时也能使其时间复杂度保持O(1),大大提高了处理并发的能力,针对回调的方式也无需额外的空间存储监听的 文件描述符。以下内容引自文尾博客:

    每一个epoll对象都有一个独立的eventpoll结构体,用于存放通过epoll_ctl方法向epoll对象中添加进来的事件。这些事件都会挂载在红黑树中,如此,重复添加的事件就可以通过红黑树而高效的识别出来(红黑树的插入时间效率是lgn,其中n为树的高度)。

    而所有添加到epoll中的事件都会与设备(网卡)驱动程序建立回调关系,也就是说,当相应的事件发生时会调用这个回调方法。这个回调方法在内核中叫ep_poll_callback,它会将发生的事件添加到rdlist双链表中。当调用epoll_wait检查是否有事件发生时,只需要检查eventpoll对象中的rdlist双链表中是否有epitem元素即可。如果rdlist不为空,则把发生的事件复制到用户态,同时将事件数量返回给用户。

    epoll整体的流程为:

  1. epoll_create()系统调用。此调用返回一个句柄,之后所有的使用都依靠这个句柄来标识。

  2. epoll_ctl()系统调用。通过此调用向epoll对象中添加、删除、修改感兴趣的事件,返回0标识成功,返回-1表示失败。

  3. epoll_wait()系统调用。通过此调用收集收集在epoll监控中已经发生的事件。


参考链接:深度理解select、poll和epoll - 云+社区 - 腾讯云


评论区
评论
{{comment.creator}}
{{comment.createTime}} {{comment.index}}楼
评论

IO多路复用模型:select、poll、epoll对比

IO多路复用模型:select、poll、epoll对比

IO多路复用

    我们平时提到的I/O几乎都是同步 阻塞模型,譬如网络请求的socket IO,在数据返回前,相应的线程或是进程将会一直 阻塞直到数据返回,比较直接的处理便是针对IO流一对一的监听,但在IO返回前,相应的系统资源会平白无故的浪费,这种处理方式会大大降低服务器的吞吐。

    如果我们用很少的线程来监听这些IO,就能实现对系统资源的更好利用,在相应的socket有数据返回时才去读取数据。这种方式被称作IO多路复用,在Linux系统中,实现IO多路复用的方式(从古老到新)有select、poll和epoll。现在很多中间件都使用epoll IO多路复用模型才因此有着很高的性能和吞吐。此处简单描述三种方式的实现和区别。


select

    select可由一个固定的进程同时监听多个 文件描述符,当有数据就绪时,监听进程会被唤醒并轮询socket(因为并不知道是哪一个准备就绪了)。基于轮询的考量,select进程默认最多监视1024个(这一数字是可以调整的) 文件描述符,用一个定长数组维护(这个数组是从用户空间额外拷贝到内核空间的,也占用了额外的内存)。select的这种轮询方式有效的提升了并发量,但是由于轮询,其性能依旧有很大限制。

poll

    poll是select的简单优化,将记录 文件描述符的数据结构由list改为了链表,解除了1024的限制,但其需要轮询的时间复杂度和空间复杂度上与select相比没有任何优化。而且在监听的 文件描述符很多时其轮询时间复杂度会更高。

epoll

    epoll为event poll,不同于轮询,epoll采用被回调的方式进行多路复用,在 文件描述符就绪后,会callback给epoll进程,这样一来不仅没有监听 文件描述符的限制,同时也能使其时间复杂度保持O(1),大大提高了处理并发的能力,针对回调的方式也无需额外的空间存储监听的 文件描述符。以下内容引自文尾博客:

    每一个epoll对象都有一个独立的eventpoll结构体,用于存放通过epoll_ctl方法向epoll对象中添加进来的事件。这些事件都会挂载在红黑树中,如此,重复添加的事件就可以通过红黑树而高效的识别出来(红黑树的插入时间效率是lgn,其中n为树的高度)。

    而所有添加到epoll中的事件都会与设备(网卡)驱动程序建立回调关系,也就是说,当相应的事件发生时会调用这个回调方法。这个回调方法在内核中叫ep_poll_callback,它会将发生的事件添加到rdlist双链表中。当调用epoll_wait检查是否有事件发生时,只需要检查eventpoll对象中的rdlist双链表中是否有epitem元素即可。如果rdlist不为空,则把发生的事件复制到用户态,同时将事件数量返回给用户。

    epoll整体的流程为:

  1. epoll_create()系统调用。此调用返回一个句柄,之后所有的使用都依靠这个句柄来标识。

  2. epoll_ctl()系统调用。通过此调用向epoll对象中添加、删除、修改感兴趣的事件,返回0标识成功,返回-1表示失败。

  3. epoll_wait()系统调用。通过此调用收集收集在epoll监控中已经发生的事件。


参考链接:深度理解select、poll和epoll - 云+社区 - 腾讯云



IO多路复用模型:select、poll、epoll对比2020-08-11鱼鱼

{{commentTitle}}

评论   ctrl+Enter 发送评论