# NIO 存在的问题

在 NIO 中,若 Selector 的轮询结果为空,也没有 wakeup() 或新消息处理,会发生空轮询导致 CPU 占用 100%。

前置知识:

JDK1.5 引入 epoll 机制。 epoll 是 linux2.6 内核的系统调用,设计目的就是替代 select、poll 线性复杂度的模型, epoll 的时间复杂度是 O (1)。 epoll 在高并发场景表现优秀。

文件描述符 (File Descriptor——fd):linux 内核利用文件描述符来访问文件,打开显存文件或新建文件时,内核会返回一个文件描述符。读写文件也需要使用文件描述符来指定待读写的文件。

文件句柄:Windows 下的概念,句柄是各种对象的标识符,它是一个非负整数,也用于定位文件数据在内存中的位置。

linux 下的文件描述符其实就相当于 windows 下的句柄。文件句柄只是 windows 众多句柄中的一种类型而已。

对于操作系统会有这样一个需求:如何在一个进程(线程)中处理多个文件,或者监听多个文件的 IO 事件。

selectpollepoll 都是操作系统中实现 IO 多路复用的方法。

# select

select 方法本质就是维护了一个文件描述符数组(32 位系统 MAX=1024,64 位 MAX=2048),依次实现 IO 多路复用,select 会监视文件描述符的变化。

select() 机制中提供了 fd_set 数据结构,实际上是一个 long 类型的数组,每个数组元素都能与以打开的文件描述符建立联系。

/proc/sys/fs/file-max指定了系统范围内所有进程可打开的文件的数量限制

select 方法被调用,首先需要将 fd_set 从用户空间拷贝到内核空间,然后内核用 poll 机制(非多路复用的 poll)返回一个 fd 准备就绪个数。方法返回后需要轮询 fd_set ,检查发生 IO 事件的 fd。

缺陷:

  • 使用轮询,效率低。
  • 会导致用户空间和内核空间频繁拷贝数据。

# poll

pollselect 很类似,只不过 poll 维护的是一个链表,单个进程监听的 fd 不再有数量限制,但是和 select 相同的轮询和复制问题依然存在。

# epoll

epoll 全称 EventPoll,是 linux 内核实现 IO 多路复用的模型。

在 Linux 中, selectorpoll 监听文件描述符 list,进行线性的查找,复杂度 O (n)。

epoll 使用了内核文件级别的回调机制,复杂度 O (1)。

epoll 基于事件驱动,给每个 fd 注册一个回调函数,当对应的 fd 有 IO 事件发生时就调用这个回调函数,将这个 fd 放入一个链表中,这样客户端可以直接从链表中获得发生 IO 事件的 fd,从而达到 O (1) 级别的监听。

epoll 有的三个系统调用:epoll_create, epoll_ctl, epoll_wait

# JDK 中 epoll 的实现

理论上无客户端连接时 Selector.select() 方法会阻塞,但空轮询 bug 导致:即使无客户端连接,NIO 照样不断的从 select 本应该阻塞的 Selector.select() 中 wake up 出来,导致 CPU100% 问题。

更新于 阅读次数 本文阅读量:

请我喝[茶]~( ̄▽ ̄)~*

Windlinxy 微信支付

微信支付

Windlinxy 支付宝

支付宝