IO模型
IO模型
Linux下的5种I/O模型
阻塞I/O模型
默认情况下,所有文件操作都是阻塞的。在进程空间中调用recvfrom,其系统调用直到数据包到达且被复制到应用进程的缓冲区中或者发生错误才返回,在此期间一直等待,进程在从调用recvfrom开始到它返回的整段时间内都是被阻塞的。
非阻塞I/O模型
进程把一个套接口设置成非阻塞是在通知内核。当所请求的I/O操作不能满足要求的时候,不把本进程投入睡眠,二十返回一个错误,也就是说当数据没有到达时并不等待,而是以一个错误返回。
I/O复用模型
Linux提供select/poll,进程通过将一个或多个fd传递给select或poll系统调用,阻塞在select:这样select/poll可以帮我们侦测许多fd是否就绪。但是select/poll是顺序扫描fd是否就绪的,而且支持的fd数量有限。Linux还提供了一个epoll系统调用,epoll基于事件驱动方式,而不是顺序扫描,当有fd就绪时,立即回调函数rollback。
信号驱动异步I/O模型
首先开启套接口信号驱动I/O功能,并通过系统调用sigaction安装一个信号处理函数(此系统调用立即返回,进程继续功能,它是非阻塞的)。当数据报准备好被读时,九尾该进程生成一个SIGIO信号。随即可以在信号处理程序中调用recvfrom来读数据报,并通知主循环数据已准备好被处理。也可以通知主循环,让它来读数据报。
异步I/O模型
告知内核启动某个操作,并让内核在整个操作完成后(包括将数据从内核复制到用户自己的缓冲区)通知我们。这种模型与信号驱动模型的主要区别是信号驱动I/O有内核通知我们何时启动一个I/O操作;异步I/O模型由内核通知我们I/O操作何时完成。
Java语言实现的I/O模型
Java Classic I/O(Blocking I/O)
Java Nob-blocking I/O(NIO)
NIO库面向Buffer的数据读取和写入
Buffer缓冲区四个属性关系:
0 <= mark <= position <= limit <= capacity
Buffer主要的API如下。
- clear,清空Buffer,将position值设置为0,limit设置为capacity,mark值设置为无效。
- flip,翻转Buffer,为读Buffer做准备。limit设置为当前的position,position值为0,mark值为无效,结合hasRemaining
- rewind,倒回Buffer,为重新读取Buffer做准备。position值为0,mark值为无效,与flip类似,结合hasRemaining
ByteBuffer有三种实现方式。
- 堆内存储数据的HeapByteBuffer
- 堆外存储数据的DirectByteBuffer
- 文件映射(数据存储到文件中)的MappedByteBuffer
TCP/IP通信的基础知识。
首先,对于TCP通信来说,每个TCP Socket在内核都有一个发送缓冲区和一个接收缓冲区,TCP的全双工的工作模式及TCP的滑动窗口边依赖于这两个独立的Buffer及此Buffer的填充状态。
对于UDP通信来说,每个UDP Socket都有一个接收缓冲区,而没有发送缓冲区,从概念上来说就是只要有数据就发,不管对方是否可以正确接收,所以不缓冲,不需要发送缓冲区。
“非阻塞”模型,它就是要解决I/O现场与Socket解耦的问题。因此,它引入了事件机制来达到解耦的目的。可以认为NIO底层中存在一个I/O调度现场,它不断扫描每个Socket的缓冲区,当发现写入缓冲区为空(或者不满)时,它会产生一个Socket可写事件,此时程序就可以把数据写入Socket,如果一次写不完,则等待下一次可写事件的通知;
NIO2及Asynchronous I/O介绍
Java7对原有的NIO进行了大幅度的升级。NIO2主要改进Classic I/O中java.io.File类对文件操作的局限性,比如,新引入了Path及Paths、Files、Directories等工具类,支持文件符号链接,扩展了文件的属性支持类型,新的API支持文件的复制与移动等。此外,NIO2带来真正意义上的Asynchronous I/O(异步I/O),具体实现AIO File和AIO Socket。
目前Linux上的AIO实现主要有两种:Posix AIO与Kernel Native AIO,前者是用户态实现的,而后者是内核态实现的,所以Kernel Native AIO的性能及前景要好于它的前辈POSIX AIO。
《架构探险:从零开始写分布式服务框架》
《架构解密:从分布式到微服务》