相关概念总结:
同步是指用户线程发起 I/O 请求后需要等待或者轮询内核 I/O 操作完成后才能继续执行;
异步是指用户线程发起 I/O 请求后仍继续执行,当内核 I/O 操作完成后会通知用户线程,或者调用用户线程注册的回调函数。
阻塞是指 I/O 操作需要彻底完成后才返回到用户空间;
非阻塞是指 I/O 操作被调用后立即返回给用户一个状态值,无需等到 I/O 操作彻底完成。
Unix架构下的五种模型:
阻塞 I/O
非阻塞 I/O
I/O 复用(select 和 poll)
信号驱动 I/O(SIGIO)
异步 I/O(Posix.1 的 aio_ 系列函数)
阻塞IO
请求无法立即完成则保持阻塞
非阻塞IO
socket 设置为 NONBLOCK(非阻塞)就是告诉内核,当所请求的 I/O 操作无法完成时,不要将进程睡眠,而是返回一个错误码(EWOULDBLOCK),这样请求就不会阻塞,但cpu会不断进行轮询,消耗大量cpu资源,数据准备好之后从内核态复制到用户态
I/O 复用(select 和 poll)
I/O 复用会用到 select 或者 poll 函数,这两个函数也会使进程阻塞,但是和阻塞 I/O 所不同的的,这两个函数可以同时阻塞多个I/O 操作。而且可以同时对多个读操作,多个写操作的 I/O 函数进行检测,直到有数据可读或可写时,才真正调用 I/O 操作函数。使用 select 以后最大的优势是用户可以在一个线程内同时处理多个 socket 的 I/O 请求。用户可以注册多个socket,然后不断地调用 select 读取被激活的 socket,即可达到在同一个线程内同时处理多个 I/O请求的目的。而在同步阻塞模型中,必须通过多线程的方式才能达到这个目的。
信号驱动IO
首先我们允许 socket 进行信号驱动 I/O,并安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个 SIGIO 信号,可以在信号处理函数中调用 I/O 操作函数处理数据。通过信号之间的通信来交互
异步IO模型
调用 aio_read 函数,告诉内核描述字,缓冲区指针,缓冲区大小,文件偏移以及通知的方式,然后立即返回。当内核将数据拷贝到缓冲区后,再通知应用程序。NIO epoll是天然的reactor模型,程序实现更容易。AIO如windows的IOCP,是异步回调的方式,开发难度很高。
前四种模型的后半部分相同 都是将数据从内核态拷贝到用户态,都是同步模型。最后一个是异步模型。
BIO NIO AIO的区别
“一个IO操作其实分成了两个步骤:发起IO请求和实际的IO操作。同步IO和异步IO的区别就在于第二个步骤是否阻塞,如果实际的IO读写阻塞请求进程,那么就是同步IO。阻塞IO和非阻塞IO的区别在于第一步,发起IO请求是否会被阻塞,如果阻塞直到完成那么就是传统的阻塞IO,如果不阻塞,那么就是非阻塞IO。同步和异步是针对应用程序和内核的交互而言的,同步指的是用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪,而异步是指用户进程触发IO操作以后便开始做自己的事情,而当IO操作已经完成的时候会得到IO完成的通知。而阻塞和非阻塞是针对于进程在访问数据的时候,根据IO操作的就绪状态来采取的不同方式,说白了是一种读取或者写入操作函数的实现方式,阻塞方式下读取或者写入函数将一直等待,而非阻塞方式下,读取或者写入函数会立即返回一个状态值。所以,IO操作可以分为3类:同步阻塞(即早期的IO操作)、同步非阻塞(NIO)、异步非阻塞(AIO)。同步阻塞:在此种方式下,用户进程在发起一个IO操作以后,必须等待IO操作的完成,只有当真正完成了IO操作以后,用户进程才能运行。JAVA传统的IO模型属于此种方式。同步非阻塞:在此种方式下,用户进程发起一个IO操作以后边可返回做其它事情,但是用户进程需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问,从而引入不必要的CPU资源浪费。其中目前JAVA的NIO就属于同步非阻塞IO。异步:此种方式下是指应用发起一个IO操作以后,不等待内核IO操作的完成,等内核完成IO操作以后会通知应用程序。