从操作系统内核看Java非阻塞IO事件检测

原创 2016年09月18日 12:10:05

非阻塞服务器模型最重要的一个特点是,在调用读取或写入接口后立即返回,而不会进入阻塞状态。在探讨单线程非阻塞IO模型前必须要先了解非阻塞情况下Socket事件的检测机制,因为对于非阻塞模式最重要的事情是检测哪些连接有感兴趣的事件发生,一般会有如下三种检测方式。

应用程序遍历socket检测

如图所示,当多个客户端向服务器请求时,服务器端会保存一个socket连接列表,应用层线程对socket列表进行轮询尝试读取或写入。对于读取操作,如果成功读取到若干数据则对读取到的数据进行处理,读取失败则下个循环再继续尝试;对于写入操作,先尝试将数据写入指定的某个socket,写入失败则下个循环再继续尝试。

这里写图片描述

这样看来,不管多少个socket连接都可以被一条线程管理起来,一条线程负责遍历这些socket列表,不断地尝试读取或写入数据,很好地利用了阻塞的时间,处理能力得到提升。但这种模型需要在应用程序中遍历所有的socket列表,同时需要处理数据的拼接,连接空闲时可能也会占用较多CPU资源,不适合实际使用。对此改进是使用事件驱动的非阻塞方式。

内核遍历socket的事件检测

这种方式将socket的遍历工作交给了操作系统内核,对socket遍历的结果组织成一系列的事件列表并返回应用层处理。对于应用层,他们需要处理的对象就是这些事件,这就是其中一种事件驱动的非阻塞方式的实现。

如图所示,服务器端有多个客户端连接,应用层向内核请求读写事件列表。内核遍历所有socket并生成对应的可读列表readList和可写列表writeList,readList标明了每个socket是否可读,例如socket1的值为1,表示可读,socket2的值为0,表示不可读。writeList则标明了每个socket是否可写。应用层遍历读写事件列表readList和writeList,做相应的读写操作。

这里写图片描述

内核遍历socket的方式已经不用在应用层对所有socket进行遍历,将遍历工作下移到内核层,这种方式有助于提高检测效率。但是它需要将所有连接的可读事件列表和可写事件列表传到应用层,假如socket连接数量大起来的话,列表从内核复制到应用层也是不小的开销。另外,当活跃连接较少时,内核与应用层之间存在很多无效的数据拷贝,因为它是将活跃和不活跃的连接状态都拷贝到应用层了。

内核基于回调的事件检测

通过遍历的方式检测socket是否可读可写是一种效率比较低的方式,不管是在应用层遍历还是在内核遍历。所以需要另外一种机制来优化遍历的方式,那就是回调函数。内核中socket都对应一个回调函数,当客户端往socket发送数据时,内核从网卡接收数据后就会调回调函数将此socket作为可读事件加入到事件列表中。应用层获取此事件列表即可得到所有感兴趣的事件。

如图所示,服务器端有多个客户端socket连接,首先应用层告诉内核每个socket感兴趣的事件;接着当客户端发送数据过来时,对应会有一个回调函数,内核从网卡复制数据成功后即调回调函数将socket1作为可读事件event1加入到事件列表,同样地,内核发现网卡可写时就将socket2作为可写事件event2添加到事件列表中;最后,应用层向内核请求读写事件列表,内核将包含了event1和event2的事件列表返回应用层,应用层通过遍历事件列表得知socket1有数据待读取,于是进行读操作,而socket2则可以写入数据。

这里写图片描述

这种方式由操作系统内核维护客户端的所有连接并通过回调函数不断更新可读可写事件列表,而应用层线程只要遍历这些事件列表即可知道可读取或可写入的连接,进而对这些连接进行读写操作。极大提高了检测效率,自然处理能力也更强。

上面介绍了三种非阻塞的事件检测机制,对于Java来说,非阻塞IO的实现完全是基于操作系统内核的非阻塞IO,它将操作系统的非阻塞IO的差异屏蔽了并提供统一的API,让我们不必关心操作系统。JDK会帮我们选择非阻塞IO的实现方式,例如对于Linux系统,在支持epoll的情况下JDK会优先选择用epoll实现Java的非阻塞IO。这种非阻塞方式的事件检测机制就是效率最高的“内核基于回调的事件检测”。

========广告时间========

公众号的菜单已分为“分布式”、“机器学习”、“深度学习”、“NLP”、“Java深度”、“Java并发核心”、“JDK源码”、“Tomcat内核”等,可能有一款适合你的胃口。

鄙人的新书《Tomcat内核设计剖析》已经在京东销售了,有需要的朋友可以购买。感谢各位朋友。

为什么写《Tomcat内核设计剖析》

=========================

版权声明:本文为博主原创文章,未经博主允许不得转载。

从JDK源码角度看线程池原理

“池”技术对我们来说是非常熟悉的一个概念,它的引入是为了在某些场景下提高系统某些关键节点性能,最典型的例子就是数据库连接池,JDBC是一种服务供应接口(SPI),具体的数据库连接实现类由不同厂商实现,...

SpringMVC札集(09)——拦截器

SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。在此,以示例的方式讲解拦截器的处理。...
  • lfdfhl
  • lfdfhl
  • 2017年08月01日 16:04
  • 909

JAVA非阻塞NIO_IO2

  • 2017年11月13日 13:56
  • 2.01MB
  • 下载

Linux内核开发之阻塞非阻塞IO----轮询操作

“小王,来聊聊,今天面试的情况怎么样,应该挺顺利的吧..”看着小王平淡的眉头,我问道。 “唉,别提了,你说,我的运气咋这差呢,面试前你不是给我讲了有关阻塞的问题吗,我见了面试官是吧,还跟他好好的用今...

内核对轮询IO(阻塞/非阻塞)的实现

应用层对设备文件的读写,常常会用到轮询操作。所谓轮询,就是指的是在读写时,若暂时无法读写,则卡在那里,直到可以读写。...

如何理解Node.js中 单线程、非阻塞IO、事件驱动

Node.js中单线程就相当于火车站售票厅只开一个售票窗口,那么所有的人都要通过这个窗口去买票,如果说这些人不排队都争抢这个窗口,可想而知,每一个人想买到票很难,还有一个就是我们经常做公交车,公交车师...

java非阻塞式IO NIO 初步认识

NIO 初认识 参加工作今年也已经是第三个年头了.  这是我的第一次(第一次决定写博客). 走出学校后我一直从事的是后台web开发.  废话不多说, 直接进入正题吧. NIO 到底是什么一...

Java 非阻塞式的高伸缩性IO通信 学习demo

本示例是通过Java实现的非阻塞式高性能IO通信的例子。通信的交互协议: 服务端给每个accept到的SocketChannel发送一条欢迎信息(Welcome to Log on!) 客户端连接在建...

Java网络编程 - 非阻塞IO模型

1. Java NIO简介NIO(Non-Block I/O)是在JDK1.4之后引入的。NIO弥补了同步阻塞I/O的缺点。提供了高速、面向块的I/O。 在NIO库中,所有的数据都是用缓冲区来处理的...

【转载】Java NIO(非阻塞IO) API介绍

转载自:http://blog.csdn.net/daijialin/article/details/231384 在JDK 1.4以前,Java的IO操作集中在java.io这个包中,是基于流的阻...
  • SMCwwh
  • SMCwwh
  • 2012年01月09日 11:02
  • 6534
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:从操作系统内核看Java非阻塞IO事件检测
举报原因:
原因补充:

(最多只允许输入30个字)