Netty学习之路(一)- 同步与异步IO

本篇博客主要是讲一些基础,记录我的学习过程,同时尝试养成写博客的习惯。内容基本来自Netty权威指南加上一丢丢的个人理解。。。。

I/O基础入门

在jdk1.4以前,java对i/o的支持并不完善,开发人员在开发高性能i/o时会遇到巨大的挑战与困难,主要问题如下:

  • 没有数据缓存区,i/o性能存在问题
  • 没有c或c++中Channel概念,只有输入输出流
  • 同步阻塞I/O(BIO)会导致通信线程长时间阻塞
  • 支持的字符集有限,硬件移植能力差

在java支持异步i/o之前,高性能服务端开发领域一直被c和c++占据,java同步i/o一直
被大家所诟病。

Linux网络I/O模型简介

linux将所有外部设备都看成文件来操作,无论是对文件还是套接字(socket)的读写都会返回一个描述符(指向内核中的一个结构体,结构体包括文件路径,数据区等属性)。应用程序要为因特网通信而创建一个套接字(socket)时,操作系统就返回一个小整数作为描述符(descriptor)来标识这个套接字。然后,应用程序以该描述符作为传递参数,通过调用函数来完成某种操作(例如通过网络传送数据或接收输入的数据)。

当应用程序要创建一个套接字时,操作系统就返回一个小整数作为描述符,应用程序则使用这个描述符来引用该套接字,然后需要I/O请求的应用程序请求操作系统打开一个文件。操作系统就创建一个文件描述符提供给应用程序访问文件。从应用程序的角度看,文件描述符是一个整数,应用程序可以用它来读写文件

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

  1. 阻塞I/O模型:应用进程被阻塞,直到数据复制到应用进程缓冲区中才返回。应该注意到,在阻塞的过程中,其它程序还可以执行,因此阻塞不意味着整个操作系统都被阻塞。因为其他程序还可以执行,所以不消耗 CPU 时间,这种模型的 CPU 利用率效率会比较高。
  2. 非阻塞I/O:应用进程执行系统调用之后,内核返回一个错误码。应用进程可以继续执行,但是需要不断的执行系统调用来获知 I/O 是否完成,这种方式称为轮询(polling)。由于 CPU 要处理更多的系统调用,因此这种模型的 CPU 利用率是比较低的。
  3. I/O复用:使用 select 或者 poll 等待数据,并且可以等待多个套接字中的任何一个变为可读。这一过程会被阻塞,当某一个套接字可读时返回,之后再使用 recvfrom 把数据从内核复制到进程中。它可以让单个进程具有处理多个 I/O 事件的能力。又被称为 Event Driven I/O,即事件驱动 I/O。如果一个 Web 服务器没有 I/O 复用,那么每一个 Socket 连接都需要创建一个线程去处理。如果同时有几万个连接,那么就需要创建相同数量的线程。相比于多进程和多线程技术,I/O 复用不需要进程线程创建和切换的开销,系统开销更小。
  4. 信号驱动I/O模型:应用进程使用 sigaction 系统调用,内核立即返回,应用进程可以继续执行,也就是说等待数据阶段应用进程是非阻塞的。内核在数据到达时向应用进程发送 SIGIO 信号,应用进程收到之后在信号处理程序中调用 recvfrom 将数据从内核复制到应用进程中。相比于非阻塞式 I/O 的轮询方式,信号驱动 I/O 的 CPU 利用率更高。
  5. 异步I/O:应用进程执行 aio_read 系统调用会立即返回,应用进程可以继续执行,不会被阻塞,内核会在所有操作完成之后向应用进程发送信号。异步 I/O 与信号驱动 I/O 的区别在于,异步 I/O 的信号是通知应用进程 I/O 完成,而信号驱动 I/O 的信号是通知应用进程可以开始 I/O。

猜你喜欢

转载自blog.csdn.net/PH15045125/article/details/83620668