阶段1 read sync blocking (BIO)
linux kernel只提供同步阻塞的read系统调用
问题:系统性能不能最大发挥,因为没有数据也会阻塞。
阶段2 read sync non-blocking (NIO)
提供了非阻塞的read系统调用
问题:如果用户进程要查询1000个file descriptors,就会有1000次系统调用(用户态/核心态过多切换,导致性能浪费)
阶段3 select sync non-blocking (NIO)
提供了select系统调用,即将上例中的1000个fd传给select,让kernel选择并返回哪些fd可读写,再使用read/write操作数据。
select 是一个主动模型,需要线程自己通过一个集合存放所有的 Socket,然后发生 I/O 变化的时候遍历。在 select 模型下,操作系统不知道哪个线程应该响应哪个事件,而是由线程自己去操作系统看有没有发生网络 I/O 事件,然后再遍历自己管理的所有 Socket,看看这些 Socket 有没有发生变化。
问题:返回可读写fd还是有用户态/核心态切换操作
阶段4 mmap sync non-blocking (NIO)
引入mmap系统调用,这样用户态/核心态通过映射文件交换fd数据
阶段5 poll (NIO)
poll 提供了更优质的编程接口,但是本质和 select 模型相同。因此千级并发以下的 I/O,可以考虑 select 和 poll,但是如果出现更大的并发量,就需要用 epoll 模型。
阶段6 epoll red-black tree (NIO)
epoll 模型在操作系统内核中提供了一个中间数据结构,这个中间数据结构会提供事件监听注册,以及快速判断消息关联到哪个线程的能力(红黑树实现)。
因此在高并发 I/O 下,可以考虑 epoll 模型,它的速度更快,开销更小。