Categories
程式開發

NIO 看破也說破(三)—— 不同的IO模型


上兩節我們提到了select 和 poll函數,查看man手冊:


SELECT(2) Linux Programmer's Manual SELECT(2)

NAME
select, pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO - synchronous I/O multiplexing

POLL(2) Linux Programmer's Manual POLL(2)

NAME
poll, ppoll - wait for some event on a file descriptor

SYNOPSIS
#include

synchronous I/O multiplexing中文解釋是同步的多路復用,因此select 是一個同步的I/O多路復用模式。 Unix共五種I/O模型:

阻塞I/O非阻塞I/OI/O多路復用信號驅動異步I/O

信號驅動和真正的異步I/O並不常用,我們重點說一下前三個。

阻塞和非阻塞的概念描述的是用戶線程調用內核IO操作的方式:阻塞是指IO操作需要徹底完成後才返回到用戶空間;而非阻塞是指IO操作被調用後立即返回給用戶一個狀態值,無需等到IO操作徹底完成

阻塞I/O

ServerSocket server = new ServerSocket(8080);
while (true) {
Socket socket = server.accept();
System.out.println("链接端口:" + socket.getPort());
InputStream inputStream = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String str = null;
while ((str = reader.readLine()) != null) {
System.out.println("接受:" + str);
socket.getOutputStream().write("okn".getBytes());
socket.getOutputStream().flush();
if ("over".equals(str)) {
System.out.println("要关闭了");
socket.close();
break;
}
}
System.out.println("===========");
}

當有數據獲取時,用戶線程要釋放cpu,直到數據由內核處理完成,整個過程用戶線程是阻塞的。

NIO 看破也說破(三)—— 不同的IO模型 1

用戶線程調用內核IO操作,需要等IO徹底完成後才返回到用戶空間,因此是阻塞IO

非阻塞I/O

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(8888));
serverSocketChannel.configureBlocking(false);
while (true) {
SocketChannel socketChannel = serverSocketChannel.accept();
if (socketChannel == null) {
System.out.println("没有链接 ");
continue;
}
System.out.printf("新链接,端口是 %s", ((InetSocketAddress) socketChannel.
getRemoteAddress()).getPort());
ByteBuffer ds = ByteBuffer.allocate(10);
socketChannel.read(ds);
System.out.println("接受数据");
}

NIO 看破也說破(三)—— 不同的IO模型 2

IO操作被調用後立即返回給用戶一個狀態值,無需等到IO操作徹底完成,因此是非阻塞的。

I/O多路復用

非阻塞IO中需要用戶線程在每個IO通路上,各自不斷輪詢IO狀態,來判斷是否有可處理的數據。如果把一個連接的可讀可寫事件剝離出來,使用單獨的線程來對其進行管理。多個IO通路,都復用這個管理器來管理socket狀態,這個叫I/O多路復用。

NIO 看破也說破(三)—— 不同的IO模型 3

多路復用在內核中提供了select,poll,epoll三種方式:

select

原理示意

NIO 看破也說破(三)—— 不同的IO模型 4

特點

只能處理有限(不同系統參數:1024/2048)個socket

select監控socket時不能準確告訴用戶是哪個socket有可用數據,需要輪詢判斷

poll

原理示意

NIO 看破也說破(三)—— 不同的IO模型 5

特點

與select沒區別

採用鍊錶實現,取消了文件個數的限制

epoll

原理示意

NIO 看破也說破(三)—— 不同的IO模型 6

epoll_wait 直接檢查鍊錶是不是空就知道是否有文件描述符準備好了

fd 上的事件發生時,與它對應的回調函數就會被調用把fd 加入鍊錶,其他處於“空閒的”狀態的則不會被加入

epoll從上面鍊錶中獲取有事件發生的fd

epoll準確的表述應該是I/O事件通知器:

EPOLL(7) Linux Programmer's Manual EPOLL(7)

NAME
epoll - I/O event notification facility

SYNOPSIS
#include

特點

沒有最大連接限制

可以直接告訴用戶程序哪一個,哪個連接有數據了

同步異步的概念

同步和異步的概念描述的是用戶線程與內核的交互方式:

同步是指用戶線程發起IO請求後需要等待或者輪詢內核IO操作完成後才能繼續執行;異步是指用戶線程發起IO請求後仍繼續執行,當內核IO操作完成後會通知用戶線程,或者調用用戶線程註冊的回調函數。

因此 阻塞I/O,非阻塞I/O,I/O多路復用,都屬於同步調用。只有實現了特殊API的AIO才是異步調用,之後單開一篇講解。

AIO(7) Linux Programmer's Manual AIO(7)

NAME
aio - POSIX asynchronous I/O overview

DESCRIPTION
The POSIX asynchronous I/O (AIO) interface allows applications to initiate one or more I/O operations that are performed asyn‐
chronously (i.e., in the background). The application can elect to be notified of completion of the I/O operation in a variety of
ways: by delivery of a signal, by instantiation of a thread, or no notification at all.

系列:

NIO 看破也說破(一)—— Linux/IO 基礎

NIO 看破也說破(二)—— Java 中的兩種 BIO

下一篇,java中reactor,簡易的netty實現