关闭

Java NIO 1:I/O模型概述

标签: IONIOjava
180人阅读 评论(0) 收藏 举报
分类:

I/O模型

在开始NIO的学习之前,先对I/O的模型有一个理解,这对NIO的学习是绝对有好处的。我画一张图,简单表示一下数据从外部磁盘向运行中进程的内存区域移动的过程:

这张图片明显忽略了很多细节,只涉及了基本操作,下面分析一下这张图。

 

用户空间和内核空间

一个计算机通常有一定大小的内存空间,如一台计算机有4GB的地址空间,但是程序并不能完全使用这些地址空间,因为这些地址空间是被划分为用户空间和内核空间的。程序只能使用用户空间的内存,这里所说的使用是指程序能够申请的内存空间,并不是真正访问的地址空间。下面看下什么是用户空间和内核空间:

1、用户空间

     用户空间是常规进程所在的区域,什么是常规进程,打开任务管理器看到的就是常规进程:

     

     JVM就是常规进程,驻守于用户空间,用户空间是非特权区域,比如在该区域执行的代码不能直接访问硬件设备。

2、内核空间

     内核空间主要是指操作系统运行时所使用的用于程序调度、虚拟内存的使用或者连接硬件资源等的程序逻辑。内核代码有特别的权利,比如它能与设备控制器通讯,控制着整个用于区域进程的运行状态。和I/O相关的一点是:所有I/O都直接或间接通过内核空间

那么,为什么要划分用户空间和内核空间呢?这也是为了保证操作系统的稳定性和安全性。用户程序不可以直接访问硬件资源,如果用户程序需要访问硬件资源,必须调用操作系统提供的接口,这个调用接口的过程也就是系统调用。每一次系统调用都会存在两个内存空间之间的相互切换,通常的网络传输也是一次系统调用,通过网络传输的数据先是从内核空间接收到远程主机的数据,然后再从内核空间复制到用户空间,供用户程序使用。这种从内核空间到用户控件的数据复制很费时,虽然保住了程序运行的安全性和稳定性,但是牺牲了一部分的效率。

最后,如何分配用户空间和内核空间的比例也是一个问题,是更多地分配给用户空间供用户程序使用,还是首先保住内核有足够的空间来运行,还是要平衡一下。在当前的Windows 32位操作系统中,默认用户空间:内核空间的比例是1:1,而在32位Linux系统中的默认比例是3:1(3GB用户空间、1GB内核空间)。

 

进程执行I/O操作的步骤

缓冲区,以及缓冲区如何工作,是所有I/O的基础。所谓"输入/输出"讲的无非也就是把数据移入或移出缓冲区。

进程执行I/O操作,归结起来,就是向操作系统发出请求,让它要么把缓冲区里的数据排干净(写),要么用数据把缓冲区填满(读)。进程利用这一机制处理所有数据进出操作,操作系统内部处理这一任务的机制,其复杂程度可能超乎想像,但就概念而言,却非常直白易懂,从上面的图,可以总结一下进程执行I/O操作的几步:

1、进程使用底层函数read(),建立和执行适当的系统调用,要求其缓冲区被填满,此时控制权移交给内核

2、内核随即向磁盘控制硬件发出命令,要求其从磁盘读取数据

3、磁盘控制器和数据直接写入内核内存缓冲区,这一步通过DMA完成,无需主CPU协助。这里多提一句,关于DMA,可以百度一下,它是现代电脑的重要特色,它允许不同速度的硬件装置来沟通,而不需要依赖于CPU的大量中断负载,大大提升了整个系统的效率

4、一盘磁盘控制器把缓冲区填满,内核随即把数据从内核空间的临时缓冲区拷贝到进程执行read()调用时指定的缓冲区

5、进程从用户空间的缓冲区中拿到数据

当然,如果内核空间里已经有数据了,那么该数据只需要简单地拷贝出来即可。至于为什么不能直接让磁盘控制器把数据送到用户空间的缓冲区呢?最简单的一个理由就是,硬件通常不能直接访问用户空间。

 

同步和异步、阻塞和非阻塞

有了上面对于I/O的解读,我们来看一下同步和异步、阻塞和非阻塞两组概念的区别,主要二者在关注点上有所不同。

1、同步和异步

同步和异步这个概念比较广,不仅仅是在I/O,其他的还有诸如同步调用/异步调用、同步请求/异步请求,都是一个意思。同步和异步,关注的是消息通信机制

所谓同步,就是发出一个"调用请求"时,在没有得到结果之前,该"调用请求"就不返回,但是一旦调用返回就得到返回值了。换句话说,就是由"调用者"主动等待"调用"的结果。像我们平时写的,方法A调用Math.random()方法、方法B调用String.substring()方法都是同步调用,因为调用者主动在等待这些方法的返回。

所谓异步,则正好相反,"调用"发出之后,这个调用就直接返回了,所有没有返回结果。换句话说,当一个异步调用请求发出之后,调用者不会立刻得到结果,因此异步调用适用于那些对数据一致性要求不是很高的场景,比如模块A更新了缓存中的某个值,模块B将某个内容分享到新浪微博,这些模块的关注点更多是"做了这件事"而不是"做了这件事是否马上成功",用分布式的话说,就是牺牲了系统的强一致性而提高了整个系统的可用性及分区容错性。如果这种场景下,我们希望获取异步调用的结果,"被调用者"可以通过状态、通知来通知调用者,或通过回调函数处理这个调用,对应Java中的有Future/FutureTask、wait/notify。

2、阻塞和非阻塞

阻塞和非阻塞关注的是程序在等待调用结果时的状态

阻塞调用指的是调用结果返回之前,当前线程会被挂起,调用线程只有在得到结果之后才会返回。

非阻塞调用指的是在不能立即得到结果之前,该调用不会阻塞当前线程。

 

Linux网络I/O模型

由于绝大多数的Java应用都部署在Linux系统上,因此这里谈一下Linux网络I/O模型。

Linux的内核将所有外部设备都看做一个文件来操作,对一个文件的读写操作会调用内核提供的系统命令,返回一个file descriptor(fd,文件描述符)。而对一个Socket的读写也会有相应的描述符,称为Socketfd(Socket描述符),描述符就是一个数字,它指向内核中的一个结构体(结构体,C/C++数据类型,类似Java中的类,存储各种不同类型的数据,这里存储的是文件路径、数据区等一些属性)。

根据UNIX网络编程对I/O模型的分类,UNIX提供了5种I/O模型,分别为:

1、阻塞I/O模型

阻塞I/O模型就是最常用的I/O模型,缺省情况下所有的文件操作都是阻塞的,以Socket来讲解此模型:在用户空间中调用recvfrom,其系统调用直到数据包到达且被复制到应用进程的缓冲区或者发生错误时才返回,在此期间会一直等待,进程在从调用recvfrom开始到它返回的整段时间内都是被阻塞的,因此被称为阻塞I/O。

2、非阻塞I/O模型

recvfrom从用户空间到内核空间的时候,如果该缓冲区没有数据的话,就直接返回一个EWOULDBOCK错误,一般都对非阻塞I/O模型进行轮询检查这个状态,看内核空间是不是有数据到来,有数据到来则从内核空间复制数据到用户空间。

3、I/O复用模型

Linux提供select/poll,进程通过将一个或者多个fd传递给select或poll系统调用,阻塞在select操作上,这样select/poll可以帮助我们侦测多个fd是否处于就绪状态。select/poll是顺序扫描fd是否就绪,而且支持的fd数量有限,因此它的使用受到了一些制约。Linux还提供了一个epoll系统调用,epoll使用基于事件驱动方式替代顺序扫描,因此性能更高。当有fd就绪时,立即会掉函数rollback。

4、信号驱动I/O模型

首先开启Socket信号驱动I/O功能,并通过系统调用sigaction执行一个信号处理函数(此系统调用立即返回,进程继续工作,它是非阻塞的)。当数据准备就绪时,就为进程生成一个SIGIO信号,通过信号会掉通知应用程序调用recvfrom来读取数据,并通知主循环函数来处理数据。

5、异步I/O

告知内核启动某个操作,并让内核在整个操作完成后(包括将数据从内核复制到用户自己的缓冲区)通知开发者。这种模型与信号驱动I/O模型的主要区别是:信号驱动I/O模型由内核通知开发者何时可以开始一个I/O操作,异步I/O模型由内核通知开发者I/O操作何时已经完成

0
0
查看评论

Unix中的I/O模型和Java NIO

Unix网络编程中的五种I/O模型 阻塞式I/0、非阻塞I/O、I/O复用、信号驱动式I/O、异步I/O 以一个网络输入为例,一个输入操作通常包括两个不同的阶段: 等待数据准备好,将数据从内核拷贝到进程中。具体过程参考下面的图。 阻塞式I/O 默认情况下的I/O模型都是阻塞式I...
  • cauchyweierstrass
  • cauchyweierstrass
  • 2015-12-06 18:47
  • 2156

JAVA通信的三种I/O模型

BIO(阻塞IO),也就是传统的IO,在进行数据读取时,在数据准备好之前,线程一直挂起,而数据从准备好,需经过硬盘,OS内核(Kernel),JVM,这时一个漫长的过程,但是每一个SOCKET连接,都需要一个线程来监听,对于传输小数据的短连接问题还不大,如果是传输大数据的长连接,那就会有大量线程在系...
  • wanglei0622
  • wanglei0622
  • 2016-04-06 15:36
  • 557

Java I/O 模型的演进

什么是同步?什么是异步?阻塞和非阻塞又有什么区别?本文先从 Unix 的 I/O 模型讲起,介绍了5种常见的 I/O 模型。而后再引出 Java 的 I/O 模型的演进过程,并用实例说明如何选择合适的 Java I/O 模型来提高系统的并发量和可用性。
  • kkkloveyou
  • kkkloveyou
  • 2016-03-01 21:17
  • 1467

I/O 多路复用之select、poll、epoll实现原理及对比总结

select,poll,epoll都是IO多路复用的机制。I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写...
  • xx_yTm
  • xx_yTm
  • 2017-01-31 23:31
  • 279

I/O模型概述

I/O模型 在开始NIO的学习之前,先对I/O的模型有一个理解,这对NIO的学习是绝对有好处的。我画一张图,简单表示一下数据从外部磁盘向运行中进程的内存区域移动的过程: 这张图片明显忽略了很多细节,只涉及了基本操作,下面分析一下这张图。 用户空间和内核空间 一个计算机通常有一定大小的...
  • zhangyuan19880606
  • zhangyuan19880606
  • 2016-04-15 15:08
  • 166

Java 7之异步I/O第2篇 - I/O操作之NIO

在NIO.2的文件I/O中,Path表示文件系统中的位置。这个Path类似于java.io.File类对象,并不表示一个已经存在的实际文件或目录,所以如果你想创建一个Path来表示即将要创建的目录或文件是完全可以的。但是要注意,如果这个文件不存在,读取时就会出现错误。 在File中已经介绍过,Fil...
  • mazhimazh
  • mazhimazh
  • 2014-01-27 09:27
  • 4824

简述Linux下的5种I/O模型

在说正文之前我觉得有必要解释一下同步(synchronous)IO,异步(asynchronous)IO,阻塞(blocking)IO和非阻塞(non-blocking)IO到底是什么,有什么区别:
  • Sun_flower77
  • Sun_flower77
  • 2017-05-31 23:25
  • 318

多路复用I/O模型之select

1 .所谓I/O多路复用是指内核一旦发现进程指定的一个或者多个I/O条件准备读取,它就通知该进程。I/O多路复用适用如下场合:  (1)当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/O复用。  (2)当一个客户同时处理多个套接口时,而这种情况是可能的,但很少出现。  (3)如果...
  • lixiaogang_theanswer
  • lixiaogang_theanswer
  • 2017-06-16 17:08
  • 200

【重要】Linux I/O模型

socket阻塞与非阻塞,同步与异步、I/O模型 分类: c/c++ socket网络编程2012-04-12 16:35 32926人阅读 评论(22) 收藏 举报 socketsocketswindowsapi服务器...
  • chenchong_219
  • chenchong_219
  • 2014-06-29 15:10
  • 1639

Mina NIO与BIO了解及Telnet简单测试-Getting Started

1.1.1. NIO回顾 1.1.1.1. NIO回顾    NIO API是java 1.4引入的,已经被大多数应用系统所采用。NIO API包含了非阻塞non-blocking应用操作。   java.nio.*包中包含了以下关键的结构...
  • boonya
  • boonya
  • 2016-06-01 16:10
  • 2758
    个人资料
    • 访问:46410次
    • 积分:833
    • 等级:
    • 排名:千里之外
    • 原创:19篇
    • 转载:112篇
    • 译文:0篇
    • 评论:3条
    文章分类
    最新评论