I/O(Input/Output)是计算机科学中指计算机和外部设备进行数据交换的过程。I/O模型是指用于治理过程和设施之间数据传输的技术。
io读写的基本原理操作系统将内存(虚拟内存)划分为两局部:一部分是内核空间(Kernel-Space),另一部分是用户空间(User-Space)应用程序不容许间接在内核空间区域进行读写,也不容许间接调用内核代码定义的函数。每个应用程序过程都有一个独自的用户空间,对应的过程处于用户态,用户态过程不能拜访内核空间中的数据,也不能间接调用内核函数,因而须要将过程切换到内核态能力进行零碎调用。
上面一个read或者一次write指令的大抵流程:通过零碎调用,抉择不同的内核函数进行状态切换,而后把内核缓冲区的数据复制到用户缓冲区中,(内核缓冲区是惟一的)
io的模型1.同步阻塞IO首先,解释一下阻塞与非阻塞。阻塞IO指的是须要内核IO操作彻底实现后才返回到用户空间执行用户程序的操作指令。“阻塞”指的是用户程序(发动IO申请的过程或者线程)的执行状态。能够说传统的IO模型都是阻塞IO模型,并且在Java中默认创立的socket都属于阻塞IO模型。其次,解释一下同步与异步。简略来说,能够将同步与异步看成发动IO申请的两种形式。同步IO是指用户空间(过程或者线程)是被动发动IO申请的一方,零碎内核是被动接管方。异步IO则反过来,零碎内核是被动发动IO申请的一方,用户空间是被动接管方。同步阻塞IO(Blocking IO)指的是用户空间(或者线程)被动发动,须要期待内核IO操作彻底实现后才返回到用户空间的IO操作。在IO操作过程中,发动IO申请的用户过程(或者线程)处于阻塞状态。
2. 同步非阻塞IO非阻塞IO(Non-Blocking IO,NIO)指的是用户空间的程序不须要期待内核IO操作彻底实现,能够立刻返回用户空间去执行后续的指令,即发动IO申请的用户过程(或者线程)处于非阻塞状态,与此同时,内核会立刻返回给用户一个IO状态值。阻塞和非阻塞的区别是什么呢?阻塞是指用户过程(或者线程)始终在期待,而不能做别的事件;非阻塞是指用户过程(或者线程)取得内核返回的状态值就返回本人的空间,能够去做别的事件。在Java中,非阻塞IO的socket被设置为NONBLOCK模式。阐明同步非阻塞IO也能够简称为NIO,然而它不是Java编程中的NIO。Java编程中的NIO(New IO)类库组件所归属的不是根底IO模型中的NIO模型,而是IO多路复用模型。同步非阻塞IO指的是用户过程被动发动,不须要期待内核IO操作彻底实现就能立刻返回用户空间的IO操作。在IO操作过程中,发动IO申请的用户过程(或者线程)处于非阻塞状态。
3. IO多路复用为了进步性能,操作系统引入了一种新的零碎调用,专门用于查问IO文件描述符(含socket连贯)的就绪状态。在Linux零碎中,新的零碎调用为select/epoll零碎调用。通过该零碎调用,一个用户过程(或者线程)能够监督多个文件描述符,一旦某个描述符就绪(个别是内核缓冲区可读/可写),内核就可能将文件描述符的就绪状态返回给用户过程(或者线程),用户空间能够依据文件描述符的就绪状态进行相应的IO零碎调用。IO多路复用(IO Multiplexing)属于一种经典的Reactor模式实现,有时也称为异步阻塞IO,Java中的Selector属于这种模型。
4. 异步IO异步IO(Asynchronous IO,AIO)指的是用户空间的线程变成被动接收者,而内核空间成为被动调用者。在异步IO模型中,当用户线程收到告诉时,数据曾经被内核读取结束并放在了用户缓冲区内,内核在IO实现后告诉用户线程间接应用即可。异步IO相似于Java中典型的回调模式,用户过程(或者线程)向内核空间注册了各种IO事件的回调函数,由内核去被动调用。Java AIO 也被称为 NIO2.0,提供了异步I/O的形式,用法和规范的I/O有十分大的差别。
5.半同步半阻塞半异步IO目前在Thrift中有所体现(ThreadedSelectorServer)半同步半阻塞半异步I/O是一种计算机网络通信模型,是一种折衷的解决方案,旨在均衡同步I/O、阻塞I/O和异步I/O的优缺点。在半同步半阻塞半异步I/O模型中,服务器端通过阻塞形式期待客户端的连贯申请,一旦建设连贯,服务器端将该连贯转换为异步形式解决申请。这样既能够解决同步I/O的性能问题,又能够保障客户端的申请不会被服务器端长时间阻塞。半同步半阻塞半异步I/O模型在性能和可靠性方面较好的均衡了同步I/O和异步I/O的优缺点,因而在许多网络系统中失去了宽泛的利用。
nio是什么?NIO是“New I/O”的缩写,是一组Java API,提供非阻塞、可扩大和高性能的I/O操作。NIO在Java 1.4中被引入,作为传统的阻塞I/O(BIO)模型的替代品,用于解决高性能和高并发利用。NIO提供了若干要害个性,如通道、缓冲区、选择器和非阻塞I/O操作,使得Java应用程序可能更无效、高效和可扩大地解决I/O操作。NIO宽泛用于各种类型的应用程序,包含服务器、代理和其余网络应用程序。
NIO 的外围原理:**缓冲区:NIO 应用缓冲区(Buffer)作为数据容器,数据读写时都是通过缓冲区进行的。通道:NIO 应用通道(Channel)作为数据的读写入口,所有的数据读写操作都是通过通道实现的。选择器:NIO 应用选择器(Selector)来治理多个通道,当通道筹备好读写操作时,选择器可能疾速地发现并告诉相应的线程。**
在 NIO 中,线程不再须要阻塞期待 I/O 操作实现,而是在读写操作实现时由选择器告诉线程,这大大提高了零碎的效率。
java版代码import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.ServerSocketChannel;import java.nio.channels.SocketChannel;import java.util.Iterator;public class NioServer { public static void main(String[] args) throws IOException { Selector selector = Selector.open(); ServerSocketChannel serverSocket = ServerSocketChannel.open(); serverSocket.bind(new InetSocketAddress("localhost", 8080)); serverSocket.configureBlocking(false); serverSocket.register(selector, SelectionKey.OP_ACCEPT); // 示意示通道感兴趣的事件类型 SelectionKey.OP_ACCEPT,从而将通道注册到选择器中。 while (true) { selector.select(); Iterator<SelectionKey> keys = selector.selectedKeys().iterator(); while (keys.hasNext()) { SelectionKey key = keys.next(); keys.remove(); if (!key.isValid()) { continue; } if (key.isAcceptable()) { ServerSocketChannel server = (ServerSocketChannel) key.channel(); SocketChannel client = server.accept(); client.configureBlocking(false); client.register(selector, SelectionKey.OP_READ); // 将一个通道注册到选择器中 } else if (key.isReadable()) { SocketChannel client = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(256); int read = client.read(buffer); if (read == -1) { client.close(); key.cancel(); continue; } buffer.flip(); client.write(buffer); } } } }}cpp版本#include <iostream>#include <string>#include <chrono>#include <thread>#include <vector>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <unistd.h>#include <fcntl.h>using namespace std;int set_nonblock(int fd) { int flags;#if defined(O_NONBLOCK) if (-1 == (flags = fcntl(fd, F_GETFL, 0))) { // 获取文件描述符 fd 的标记 flags = 0; } return fcntl(fd, F_SETFL, flags | O_NONBLOCK); // flags 和 O_NONBLOCK 标记进行按位或运算,从而将 O_NONBLOCK 标记增加到原有的标记中。设置文件描述符 fd 为非阻塞模式#else flags = 1; return ioctl(fd, FIOBIO, &flags); //文件描述符设置为非阻塞模式。ioctl 操作通常用于特定的设施驱动程序#endif}int main(int argc, char **argv) { int MasterSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); vector<int> SlaveSockets; sockaddr_in SockAddr; SockAddr.sin_family = AF_INET; SockAddr.sin_port = htons(12345); SockAddr.sin_addr.s_addr = htonl(INADDR_ANY); bind(MasterSocket, (sockaddr *)&SockAddr, sizeof(SockAddr)); set_nonblock(MasterSocket); // 将MasterSocket设置成非阻塞状态 listen(MasterSocket, SOMAXCONN); while (true) { fd_set Set; FD_ZERO(&Set); /*将set清零使汇合中不含任何fd*/ FD_SET(MasterSocket, &Set); /*将MasterSocket退出set汇合*/ for (int Slave : SlaveSockets) { FD_SET(Slave, &Set); /*将Slave注册到选择器中*/ } int Max = max(MasterSocket, *max_element(SlaveSockets.begin(), SlaveSockets.end())); select(Max + 1, &Set, NULL, NULL, NULL); // 监督的最大文件描述符加1 if (FD_ISSET(MasterSocket, &Set)) { //*MasterSocket是否在set汇合中*/ int Slave = accept(MasterSocket, 0, 0); // 期待连贯 set_nonblock(Slave); //设置非阻塞 SlaveSockets.push_back(Slave); } for (int Slave : SlaveSockets) { if (FD_ISSET(Slave, &Set)) { static char Buffer[1024]; int RecvSize = recv(Slave, Buffer, 1024, MSG_NOSIGNAL); // 从而从套接字中接收数据并将其存储到 Buffer 缓冲区中。 if ((RecvSize == 0) && (errno != EAGAIN)) { shutdown(Slave, SHUT_RDWR); close(Slave); SlaveSockets.erase(remove(SlaveSockets.begin(), SlaveSockets.end(), Slave)); } else if (RecvSize > 0) { send(Slave, Buffer, RecvSize, MSG_NOSIGNAL); // 将 } } } } return 0;}摘要:java高并发编程
...