feling.net/_posts/2023-01-11-select-epoll.md

3.0 KiB
Raw Permalink Blame History

layout title categories tags
pages select、epoll 杂谈
os
epoll

讲网络IO模型先区分下两个层级一是操作系统层二是我们的用户代码层。

一切都依赖于操作系统的内核代码的发展,

BIO

早期,操作系统只提供阻塞式的系统调用。用户代码也就只好用 “来一个socket连接就为它单独起一个线程来接收数据” 的线程模型。不单独分配线程的话,程序阻塞在那,一次就只能处理一个请求。这个阶段最突出的问题,在于大量线程的创建、回收、切换的损耗。

NIO

后来发展出了非阻塞的系统调用。也就是 accept() 方法会立即返回了只不过没有连接的时候返回的是null。recv() 也支持立即返回,可以去询问某个连接是不是可以读数据了。这解决了大量线程的创建、回收、切换的问题。用户程序可以在一个线程里循环 accept()不是null就放到列表里存起来。甚至再拮据一点就在这段循环里把列表里的连接挨个拿到操作系统那边询问是不是可以读数据了。一个线程可以维护多个连接了。这个阶段最突出的问题在于大量向操作系统询问连接是否可用的操作系统调用的消耗极大又只有少数的询问能得到肯定回复。

NIO 中的多路复用器

再后来多路复用器出现了select() 支持一次传多个连接做参数虽然还是需要挨个连接判断一遍但这个遍历过程全程在内核态完成。减少了系统调用的次数。这个阶段最突出的问题在于内核内部依然需要遍历所有连接。poll() 这个多路复用器会更新一点,但它的实现也是需要遍历所有连接。

“路” 指的是 用户程序调用操作系统的系统调用接口读取scoket 中的数据 的路

多路复用器的更新

再后来,多路复用器出了一个新的版本,叫 epoll。

epoll_create() 创建一个实例,

epoll_ctl() 往里添加要监控的连接,

epoll_wait() 去询问有没有可读的连接。

比起 select() 的直接询问epoll 的调用方式多了创建实例和添加被监控连接这两个前置步骤。在前置的步骤里它需要给这个epoll实例分配一颗红黑树和一个列表把连接放进树里。当网卡的中断来临select 需要的只是有人能把连接状态标记为可以读数据就行了。而 epoll 还多了一步,维护红黑树的节点,把这个可读的连接移动到专门的列表里。用户程序来询问的时候,直接查看可读列表的数据,不需要遍历所有节点了。

New IO 还是 Non-Blocking IO

NIO 这个词,操作系统里叫 Non-Blocking IO指的是提供了一套非阻塞的系统调用。java 里叫 New IO它伴随着操作系统网络IO的发展新封装了操作系统的调用装成统一的java的 Selector类底下实际用的是select 还是 epoll又或者是windows才有的其他多路复用器要看操作系统的支持。

  • 文章目录 {:toc}