`
longzhun
  • 浏览: 361229 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

JAVA NIO的selector的实现原理

 
阅读更多

Java NIO的核心类库多路复用器Selector就是基于epoll的多路复用技术实现的 

相比select、poll系统调用,epoll有如下优点:

1.支持一个进程打开的socket描述符(FD)不受限制,仅受限于操作系统的最大文件句柄数。 
select最大的缺陷是单个进程所打开的FD是有一定限制的,它由FD_SETSIZE设置,默认值是1024。可以选择修改这个宏后重新编译内核,但这对带来网络效率的下降。也可以选择多进程的方案(传统的Apache方案)来解决这个问题,但进程的创建成本,以及进程间的数据交换需要进行考虑。 
epoll具体支持的FD上线值可以通过cat /proc/sys/fs/file-max查看,这个值和系统的内存关系比较大。

2.I/O效率不会随着FD数目的增加而线性下降。 
当拥有一个很大的socket集合时,由于网络延时或者链路空闲,任一时刻只有少部分的socket是“活跃”的,但select/poll每次调用都会线性扫描全部的集合,导致效率线性下降。而epoll只会对活跃的socket进行操作-只有活跃的socket才会主动调用callback函数,所以只需要遍历那些被内核I/O事件异步唤醒而加入Ready队列的FD集合。

3.使用mmap加速内核与用户空间的消息传递 
epoll通过内核和用户空间mmap同一块内存来实现的。mmap()系统调用允许存放在块设备上的文件或信息的一部分映射到进程的部分地址空间。

看到这里,或许会产生一个疑问,为什么内核要和用户空间进行消息传递呢?

下面我来进行深入解析:

首先,操作系统必须完成的两个主要目标:

1,与硬件部分交互

2,为运行在计算机系统上的应用程序(即所谓的用户程序)提供执行环境。

而现代操作系统是禁止用户程序直接与底层硬件进行交互的,或者禁止直接访问任意的物理地址。为此,硬件为CPU引入了至少两种不同的执行模式:用户程序的非特权模式和内核的特权模式。Unix把它们分别称为用户态(User Mode)和内核态(Kernel Mode)。所以,每个实际的文件I/O操作必须在内核态下进行。

cpu既可以运行在用户态,也可以运行在内核态。一些CPU可以有两种以上的执行状态,如Intel80x86微处理器有四种不同的执行状态。但是,所有标准的Unix内核都仅仅利用了内核态和用户态。

一个程序执行时,大部分时间都处在用户态,只有需要内核所提供的服务时才切换到内核态。当内核满足了用户程序的请求后,它让程序又回到用户态下。

然后,访问文件的模式有多种:

1,规范模式

规范模式下文件打开后,标志O_SYNC与O_DIRECT清0,而且它的内容是由系统调用read()和write()来存取。系统调用read()将阻塞调用进程,直到数据被拷贝进用户态地址空间。但系统调用write()不同,它在数据被拷贝到页高速缓存(延迟写)后就马上结束。

2,同步模式

同步模式下文件打开后,标志O_SYNC置1。这个标志只影响写操作(读操作总是会阻塞),它将阻塞调用进程,直到数据被有效的写入磁盘。

3,内存映射模式

内存映射模式下文件打开后,应用程序发出系统调用mmap()将文件映射到内存中。因此,文件就称为RAM中的一个字节数组,应用程序就可以直接访问数组元素,而不需用系统调用read()、write()或lseek()。

4,直接I/O模式

直接I/O模式下文件打开后,标志O_DIRECT置1。任何读写操作都将数据在用户态地址空间与磁盘间直接传送而不通过页高速缓存。

5,异步模式

异步模式下,文件的访问可以有两种方法,即通过一组POSIX API或Linux特有的系统调用来实现。所谓的异步模式就是数据的传输请求不阻塞调用进程,而是在后台执行,同时应用程序继续它的正常运行。

说道内存映射,就要谈谈操作系统的内存管理了。

以80X86微处理器为例,我们必须区分以下三种不同的地址:

逻辑地址,包含在机器语言指令中用来指定一个操作数或一条指令的地址。每一个逻辑地址都由一个段(segment)和偏移量(offset)组成。

线性地址,也称虚拟地址,是一个32位无符号整数,可以用来表示高达4GB的地址。

物理地址,用于内存芯片级内存单元寻址。

内存控制单元(MMU)通过一种称为分段单元的硬件电路把一个逻辑地址转换成线性地址;接着,第二个称为分页单元的硬件电路把线性地址转换成一个物理地址。

注: 
Linux采用4KB页框大小作为标准的内存分配单元。

内存碎片的产生主要是由于请求内存的大小与分配给它的大小不匹配而造成的。一种典型的解决办法是提供按照几何分布的内存区大小,即内存区大小取决于2的幂而不取决于所存放的数据大小。这样,不管请求内存的大小是多少,都可以保证内存碎片小于50%。

内核使用一种新的资源-线性区,实现了对进程动态内存的推迟分配。当用户态进程请求动态内存时,并没有获得请求的页框,而仅仅获得对一个新的线性地址区间的使用权,而这一线性地址区间就成为进程地址空间的一部分。这一区间叫“线性区”。

进程的地址空间由允许进程使用的全部线性地址组成。内核通过所谓线性区的资源来表示线性地址区间,线性区是有起始线性地址、长度和一些访问权限来描述的。

内存映射,内核给这个进程分配一个新的线性区来映射这个文件。一个新建立的内存映射就是一个不包含任何页的线性区,当进程引用线性区中的一个地址时,缺页异常发生。

与进程地址空间有关的全部信息都包含在一个叫做内存描述符的数据结构中,类型为mm_struct。

Linux通过类型为vm_area_struct的对象实现线性区

进程所拥有的所有线性区是通过一个简单的链表按照内存地址升序链接在一起的,其中mmap字段指向链表中的第一个线性区描述符。当进程中的线性区很多时,Linux2.6使用红黑树来存储内存描述符。

进程的地址空间、它的内存描述符以及线性区链表三者之间的关系,如下图所示: 

每个Unix进程都拥有一个特殊的线性区-堆,堆用于满足进程的动态内存请求。

For most operating systems, mapping a file into memory is more expensive than reading or writing a few tens of kilobytes of data via the usual read and write methods. From the standpoint of performance it is generally only worth mapping relatively large files into memory.

JDK1.7升级了NIO类库,升级后的NIO类库被称为NIO2.0。

NIO2.0正式提供了异步文件通道和异步套接字通道的实现。对应于Unix网络编程中的事件驱动I/O - AIO。它不需要通过多路复用器-Selector对注册的通道进行轮询即可实现异步读写,从而简化了NIO的编程模型。

分享到:
评论

相关推荐

    Java NIO——Selector机制解析三(源码分析)

    NULL 博文链接:https://goon.iteye.com/blog/1775421

    Java NIO Selector用法详解【含多人聊天室实例】

    主要介绍了Java NIO Selector用法,结合实例形式分析了Java NIO Selector基本功能、原理与使用方法,并结合了多人聊天室实例加以详细说明,需要的朋友可以参考下

    Java NIO原理和使用

    Java NIO非堵塞技术实际是采取Reactor模式,或者说是Observer模式为我们监察I/O端口,如果有内容进来,会自动通知我们,这样,我们就不必开启多个线程死等,从外界看,实现了流畅的I/O读写,不堵塞了。 Java NIO...

    core-nio:nio底层实现原理(另外还有aio的功能)

    core-niojava nio 客户端和服务端交互实现主要通过长连接的方式进行数据交换,采用多路复用技术,同步非阻塞模式。主要有以下几个概念Channel(渠道,类似于高速公路可以处理很多线程io);Selector(选择器,可以...

    JAVA版基于netty的物联网高并发智能网关.rar

    然而使用JDK原生的api开发NIO,编程模型是非常复杂的,开发者需要理解Selector、Channel、ByteBuffer三大组件,此外JDK原生NIO实现上还存在一些稳定性问题。netty把三大组件交互代码封装在框架内部有效的简化了编程...

    BAT面试真题最新(涵盖全方面)

    java NIO的⼯工作原理 由⼀一个专⻔门的线程来处理理所有的 IO 事件,并负责分发。 事件驱动机制:事件到的时候触发,⽽而不不是同步的去监视事件。 线程通讯:线程之间通过 wait,notify 等⽅方式通讯。保证每次...

    Java面试高频知识点总结

    – NIO的实现原理 NIO主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector。传统 IO 基于字节流和字符流进行操作,而 NIO 基于 Channel 和 Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从...

    精通并发与 netty 视频教程(2018)视频教程

    39_NIO中Scattering与Gathering深度解析 40_Selector源码深入分析 41_NIO网络访问模式分析 42_NIO网络编程实例剖析 43_NIO网络编程深度解析 44_NIO网络客户端编写详解 45_深入探索Java字符集编解码 46_字符集编解码...

    精通并发与netty视频教程(2018)视频教程

    20_通过Apache Thrift实现Java与Python的RPC调用 21_gRPC深入详解 22_gRPC实践 23_Gradle Wrapper在Gradle项目构建中的最佳实践 24_gRPC整合Gradle与代码生成 25_gRPC通信示例与JVM回调钩子 26_gRPC服务器流式调用...

    精通并发与netty 无加密视频

    第20讲:通过Apache Thrift实现Java与Python的RPC调用 第21讲:gRPC深入详解 第22讲:gRPC实践 第23讲:Gradle Wrapper在Gradle项目构建中的最佳实践 第24讲:gRPC整合Gradle与代码生成 第25讲:gRPC通信示例与...

    java核心知识点整理.pdf

    25 JAVA8 与元数据.................................................................................................................................25 2.4. 垃圾回收与算法 .................................

    Netty原理架构解析

    本文来自于csdn,本文主要介绍了关于Netty的原理架构解析,介绍的NettyReactor模型以及服务端Netty的工作架构,希望对您的学习有所帮助。...例如熟悉Java多线程编程,因为NIO编程涉及到Reactor模式

    JAVA核心知识点整理(有效)

    25 JAVA8 与元数据.................................................................................................................................25 2.4. 垃圾回收与算法 .................................

Global site tag (gtag.js) - Google Analytics