精华内容
下载资源
问答
  • 2021-11-18 20:47:01

    目录

    常见IO模型

    1. 阻塞IO模型

    2. 非阻塞I/O模型

    3. I/O复用

    4. 信号驱动IO模型

    5. 异步IO

    一个形象的示例说明


    常见IO模型

    对于一次 IO 访问,它会经历两个阶段:等待数据准备就绪 (Waiting for the data to be ready);将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)。

    举例来说:

    读函数:分为等待系统可读真正的读

    写函数:分为等待网卡可以写真正的写

    说明:等待就绪的阻塞是不使用 CPU 的,是在“空等”。而真正的读写操作的阻塞是使用 CPU 的,真正在“干活”,而且这个过程非常快,属于 memory copy,宽带通常在 1GB/s 级别以上,可以理解为基本不耗时。

    img

    1. 阻塞IO模型

    img

    2. 非阻塞I/O模型

    非阻塞IO通过进程反复调用IO函数,采用轮询,占用CPU。

    img

    3. I/O复用

    主要是select和epoll,对一个IO端口,两次调用,两次返回,能实现对多个IO端口进行监听。多个连接共用一个等待机制。

    img

    4. 信号驱动IO模型

    首先开启接口信号驱动IO功能,通过系统调用sigation执行一个信号处理函数(此信号调用直接返回,进程继续工作)。当数据准备就绪时,生成一个signal信号,通知应用程序来取数据。

    img

    5. 异步IO

    告知内核启动某个操作,并让内核在整个操作完成的的那个之后(将数据从内核复制到用户自己的缓冲区),进行通知。和信号驱动模型的主要区别:信号驱动IO由内核通知我们合适可以开始一个IO操作,在client进行IO操作的时候需要等待,所以是同步的。异步IO模型由内核通知我们IO何时已经完成,client不需要进行IO的处理了,所以是异步的。

    img

    一个形象的示例说明

    活动:演唱会

    角色1:举办商 售票业务员

    角色2:黄牛

    角色3:小明

    角色4:送票快递员

    同步阻塞 :

    小明从家里面先到演唱会现场问售票业务员买票,但是票还没出来,IO三天以后才出来,小明直接打了个地铺睡在举办商售票大厅,一直等票出来,然后买票。

    同步非阻塞 : socket可以设置non-blocking

    小明从家里面先到演唱会现场问售票业务员买票,但是票还没出来,然后小明走了,办理其他事情去了,然后过了2个小时,又去举办商售票大厅买票来了,如果票还没有出来,小明又先去办其他事情了,重复上面的操作,直到有票可以买。

    i/o复用 java selector() => linux epoll_wait() 同步非阻塞I/O

    小明想买票看演唱会,都直接给黄牛(selector/epoll)打电话了,说帮我留意买个票,票买了通知我,我自己去取(当我接到黄牛的电话时,我需要花费整个路成的时间去读这个数据,买拿这个票),那么票没出来之前,小明完全可以做自己的事情。

    信号i/o

    小明想买票看演唱会,给举办商售票业务员说,给你留个电话,有票了请你给我打个电话通知一下,(是看人家操作系统提不提供这种功能,Linux提供,windows没有这种机制),我自己再来买票(小明完全可以做自己的事情,但是票还是需要小明自己去拿的)。

    异步非阻塞i/o Linux aio_read aio_write

    小明想买票看演唱会,给举办商售票业务员说(异步非阻塞i/o)打电话了,给你留个地址,有票了请通知快递员,把这张票送到这个地址来,当小明听到敲门声,看见快递员,就知道票好了,而且知道票好了的时候,票已经到他手上了,票不用小明自己去取(应用不用自己再去read数据了)


    以上都是学习过程中的总结,如果有错误或者有疑问,欢迎一起交流吖~~

    更多相关内容
  • 主要介绍了Java三种IO模型原理实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • IO模型的比较分析

    2021-01-07 03:51:30
    到目前为止已经将将四个IO模型都介绍完了。 那么啊阻塞IO、非阻塞IO、同步IO和异步IO的区别在哪? 阻塞IO和非阻塞IO的区别 调用blocking IO会一直block住对应的进程直到操作完成,而non-blocking IO在kernel还准备...
  • 网络IO模型:同步IO和异步IO,阻塞IO和非阻塞IO
  • windows TCP/IP 网络编程(六)5种windows网络模型(4) 重叠IO模型(b)完成例程 DEMO
  • linux io 模型

    2019-01-09 02:27:16
    linux io模型,区分阻塞非阻塞同步异步的概念,从底层理解io。
  • 本代码采用C++实现,在VS2015下开发,可以直接运行,代码中有相关的注释,如发现有问题,请不吝指教,谢谢!
  • 主要介绍了Linux 下的五种 IO 模型详细介绍的相关资料,需要的朋友可以参考下
  • IO模型介绍

    2019-02-21 11:45:19
    介绍了五种IO模型,可以快速了解IO模型相关基础知识点
  • (1)同步阻塞IO(BlockingIO):即传统的IO模型。(2)同步非阻塞IO(Non-blockingIO):默认创建的socket都是阻塞的,非阻塞IO要求socket被设置为NONBLOCK。注意这里所说的NIO并非Java的NIO(NewIO)库。(3)IO多...
  • 高性能IO模型浅析

    2018-05-05 11:22:06
    服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种: (1)同步阻塞IO(Blocking IO):即传统的IO模型。 (2)同步非阻塞IO(Non-blocking IO):默认创建的socket都是阻塞的,非阻塞IO要求socket被设置...
  • 重叠IO模型

    2013-10-21 20:21:45
    主要使用控制台程序,大致讲叙了IO模型的相关步骤以及实现C/S模型,适合初学者查看
  • 图解5种IO模型

    千次阅读 2022-02-05 21:13:35
    5种IO 模型 同步阻塞、同步非阻塞、同步多路复用、异步阻塞(没有此情况)、异步非阻塞 当调用一次 channel.read 或 stream.read 后,会切换至操作系统内核态来完成真正数据读取,而读取又分为两个阶段,分别为:...

    5种IO 模型

    请添加图片描述

    同步阻塞、同步非阻塞、同步多路复用、异步阻塞(没有此情况)、异步非阻塞

    在这里插入图片描述

    在这里插入图片描述

    当调用一次 channel.read 或 stream.read 后,会切换至操作系统内核态来完成真正数据读取,而读取又分为两个阶段,分别为:

    • 等待数据阶段
    • 复制数据阶段

    在这里插入图片描述

    阻塞 IO

    在这里插入图片描述
    在这里插入图片描述

    当用户进程调用了recvfrom这个系统调用,kernel就开始了IO的

    第一个阶段:准备数据(对于网络IO来说,很多时候数据在一开始还没有到达。比如,还没有收到一个完整的UDP包。这个时候kernel就要等待足够的数据到来)。这个过程需要等待,也就是说数据被拷贝到操作系统内核的缓冲区中是需要一个过程的。而在用户进程这边,整个进程会被阻塞(当然,是进程自己选择的阻塞)。当kernel一直等到数据准备好了,它就会将数据从kernel中拷贝到用户内存,然后kernel返回结果,用户进程才解除block的状态,重新运行起来。

    所以,BIO的特点就是在IO执行的两个阶段都被block了。

    tomcat7之前就是用BIO多线程来解决多连接

    存在的问题

    多线程模型

    每来一个客户端,就要开辟一个线程,如果来1万个客户端,那就要开辟1万个线程。

    在操作系统中用户态不能直接开辟线程,需要调用内核来创建的一个线程,

    这其中还涉及到用户状态的切换(上下文的切换),十分耗资源。

    知道问题所在了,请问如何解决??

    解决办法

    第一个办法:使用线程池

    这个在客户端连接少的情况下可以使用,但是用户量大的情况下,你不知道线程池要多大,太大了内存可能不够,也不可行。

    第二个办法:NIO(非阻塞式IO)方式

    因为read()方法堵塞了,所有要开辟多个线程,如果什么方法能使read()方法不堵塞,这样就不用开辟多个线程了,这就用到了另一个IO模型,NIO(非阻塞式IO)

    思考

    1. 每个线程分配一个连接

    2. 每个线程分配一个连接,必然会产生多个,既然是多个socket链接必然需要放入进容器,纳入统一管理

    BIO应用场景

    BIO:是 javajdk1.4前的产物,可用于网络通信及磁盘交互,由于其阻塞特性让性能成为瓶颈,也是阻塞是 IO 模型的典范,但是开发者没有在原有的基础上进行改良而是进行了新的开发 NIO,所以在一定的业务场景中也有使用,

    场景:BIO 方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,对访问响应速度没有太高要求的架构中可以考虑,优点开发简单,易上手。

    非阻塞 IO

    在这里插入图片描述

    在NIO模式中,一切都是非阻塞的:

    1. accept()方法是非阻塞的,如果没有客户端连接,就返回error
    2. read()方法是非阻塞的,如果read()方法读取不到数据就返回error,如果读取到数据时只阻塞read()方法读数据的时间

    在NIO模式中,只有一个线程

    当一个客户端与服务端进行连接,这个socket就会加入到一个数组中,隔一段时间遍历一次,

    看这个socket的read()方法能否读到数据,这样一个线程就能处理多个客户端的连接和读取了

    当用户进程发出read操作时,如果kernel中的数据还没有准备好,那么它并不会block用户进程,而是立刻返回一个error。从用户进程角度讲 ,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果。用户进程判断结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作。一旦kernel中的数据准备好了,并且又再次收到了用户进程的system call,那么它马上就将数据拷贝到了用户内存,然后返回。所以,NIO特点是用户进程需要不断的主动询问内核数据准备好了吗?

    在这里插入图片描述

    在非阻塞式 I/O 模型中,应用程序把一个套接口设置为非阻塞,就是告诉内核,当所请求的I/O操作无法完成时,不要将进程睡眠而是返回一个“错误”,应用程序基于 I/O 操作函数将不断的轮询数据是否已经准备好,如果没有准备好,继续轮询,直到数据准备好为止。

    存在的问题

    NIO成功的解决了BIO需要开启多线程的问题,NIO中一个线程就能解决多个socket,但是还存在2个问题。

    问题一:

    这个模型在客户端少的时候十分好用,但是客户端如果很多,
    比如有1万个客户端进行连接,那么每次循环就要遍历1万个socket,如果一万个socket中只有10个socket有数据,也会遍历一万个socket,就会做很多无用功,每次遍历遇到 read 返回 -1 时仍然是一次浪费资源的系统调用。

    问题二:

    而且这个遍历过程是在用户态进行的,用户态判断socket是否有数据还是调用内核的read()方法实现的,这就涉及到用户态和内核态的切换,每遍历一个就要切换一次,开销很大因为这些问题的存在。

    优缺点

    优点:

    不会阻塞在内核的等待数据过程,每次发起的 I/O 请求可以立即返回,不用阻塞等待,实时性较好。

    缺点:

    轮询将会不断地询问内核,这将占用大量的 CPU 时间,系统资源利用率较低,所以一般 Web 服务器不使用这种 I/O 模型。
    结论:让Linux内核搞定上述需求,我们将一批文件描述符通过一次系统调用传给内核由内核层去遍历,才能真正解决这个问题。IO多路复用应运而生,也即将上述工作直接放进Linux内核,不再两态转换而是直接从内核获得结果,因为内核是非阻塞的。

    多路复用

    在这里插入图片描述

    IO multiplexing就是我们说的select,poll,epoll,有些地方也称这种IO方式为event driven IO事件驱动IO。

    就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。

    可以基于一个阻塞对象,同时在多个描述符上等待就绪,而不是使用多个线程(每个文件描述符一个线程,每次new一个线程),这样可以大大节省系统资源。

    所以,I/O 多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,select()函数就可以返回。

    多路复用快的原因在于,操作系统提供了这样的系统调用,使得原来的 while 循环里多次系统调用,
    变成了一次系统调用 + 内核层遍历这些文件描述符。

    在这里插入图片描述

    select, poll, epoll 都是I/O多路复用的具体的实现

    多路复用快的原因在于,操作系统提供了这样的系统调用,使得原来的 while 循环里多次系统调用,
    变成了一次系统调用 + 内核层遍历这些文件描述符。

    epoll是现在最先进的IO多路复用器,Redis、Nginx,linux中的Java NIO都使用的是epoll。

    这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程。
    1、一个socket的生命周期中只有一次从用户态拷贝到内核态的过程,开销小
    2、使用event事件通知机制,每次socket中有数据会主动通知内核,并加入到就绪链表中,不需要遍历所有的socket

    在多路复用IO模型中,会有一个内核线程不断地去轮询多个 socket 的状态,只有当真正读写事件发送时,才真正调用实际的IO读写操作。因为在多路复用IO模型中,只需要使用一个线程就可以管理多个socket,系统不需要建立新的进程或者线程,也不必维护这些线程和进程,并且只有真正有读写事件进行时,才会使用IO资源,所以它大大减少来资源占用。

    多路I/O复用模型是利用 select、poll、epoll 可以同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有 I/O 事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll 是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。 采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络 IO 的时间消耗),且 Redis 在内存中操作数据的速度非常快,也就是说内存内的操作不会成为影响Redis性能的瓶颈
    在这里插入图片描述

    大家都用过nginx,nginx使用epoll接收请求,ngnix会有很多链接进来, epoll会把他们都监视起来,然后像拨开关一样,谁有数据就拨向谁,然后调用相应的代码处理。redis类似同理。

    Redis单线程如何处理那么多并发客户端连接,为什么单线程,为什么快

    Redis的IO多路复用

    Redis利用epoll来实现IO多路复用,将连接信息和事件放到队列中,一次放到文件事件分派器,事件分派器将事件分发给事件处理器。

    在这里插入图片描述
    Redis 是跑在单线程中的,所有的操作都是按照顺序线性执行的,但是由于读写操作等待用户输入或输出都是阻塞的,所以 I/O 操作在一般情况下往往不能直接返回,这会导致某一文件的 I/O 阻塞导致整个进程无法对其它客户提供服务,而 I/O 多路复用就是为了解决这个问题而出现

    所谓 I/O 多路复用机制,就是说通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或写就绪),能够通知程序进行相应的读写操作。这种机制的使用需要 select 、 poll 、 epoll 来配合。多个连接共用一个阻塞对象,应用程序只需要在一个阻塞对象上等待,无需阻塞等待所有连接。当某条连接有新的数据可以处理时,操作系统通知应用程序,线程从阻塞状态返回,开始进行业务处理。

    Redis 服务采用 Reactor 的方式来实现文件事件处理器(每一个网络连接其实都对应一个文件描述符)

    Redis基于Reactor模式开发了网络事件处理器,这个处理器被称为文件事件处理器。它的组成结构为4部分:

    1. 多个套接字、
    2. IO多路复用程序、
    3. 文件事件分派器、
    4. 事件处理器。

    因为文件事件分派器队列的消费是单线程的,所以Redis才叫单线程模型

    参考《Redis 设计与实现》

    在这里插入图片描述

    NIO应用场景

    NIO:javajdk1.4 后研发新产物,结合 IO 模型中的非阻塞 IO 和多路复用模型来进行优化,但是所谓的非阻塞也并非真真意义上的非阻塞,在用户态和内核态数据复制时还是有阻塞,真真的非阻塞还是 AIO,充分使用 OS 资源。

    NIO 三大组件:channel,buffer,selector 其中 channel底层封装了 socket 和 IO 类似于DMA 直接存储器的升级为 channel 的设计理念。

    Buffer 和 selector 是 NIO 性能高效的关键。

    Selector:使用多路复用模型来进行改造,让单线程拥有处理多个连接的能力,当然在连接数量较少的时候性能不及多线程+BIO,但是在连接数很多的时候性能就能很好的发挥

    Buffer:是充分利用了 java堆外内存的使用,减少内存 IO 的次数来实现高性能的所以 NIO 在磁盘文件的复制上也有很大优势,然后其中对零拷贝,磁盘文件映射都有相对应的方案。

    场景:NIO 方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,服务器需要支持超大量的长时间连接,比如 10000 个连接以上,并且每个客户端并不会频繁地发送太多数据,例如总公司的一个中心服务器需要收集全国便利店各个收银机的交易信息,只需要少量线程按需处理维护的大量长期连接,代码开发难度较大.

    Jetty、Mina、Netty 和 ZooKeeper 等都是基于 NIO 方式实现。

    异步 IO

    在这里插入图片描述

    AIO应用场景

    AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。

    5 种 I/O 模型总结

    在这里插入图片描述
    阻塞越少,理论上性能也越优。

    • 阻塞式IO,每个连接要对应一个线程单独处理,浪费资源。
    • 非阻塞式IO,需要不断的轮询,也耗费CPU资源。
    • 信号驱动式IO,在大量IO操作时会有信号队列溢出,且对于TCP而言,通知条件过多,每一个进行判断会消耗资源。
    • 异步IO,理论最优,但是目前Linux支持还不是很完善。

    因此在Linux下网络编程都以IO复用模型为主。

    零拷贝

    传统 IO 问题

    传统的 IO 将一个文件通过 socket 写出

    File f = new File("helloword/data.txt");
    RandomAccessFile file = new RandomAccessFile(file, "r");
    
    byte[] buf = new byte[(int)f.length()];
    file.read(buf);
    
    Socket socket = ...;
    socket.getOutputStream().write(buf);
    

    内部工作流程是这样的:

    在这里插入图片描述

    1. java 本身并不具备 IO 读写能力,因此 read 方法调用后,要从 java 程序的用户态切换至内核态***,去调用操作系统(Kernel)的读能力*,将数据读入内核缓冲区。这期间用户线程阻塞,操作系统使用 DMA(Direct Memory Access)来实现文件读,其间也不会使用 cpu

      DMA 也可以理解为硬件单元,用来解放 cpu 完成文件 IO

    2. 从内核态切换回用户态,将数据从内核缓冲区读入用户缓冲区(即 byte[] buf),这期间 cpu 会参与拷贝,无法利用 DMA

    3. 调用 write 方法,这时将数据从用户缓冲区(byte[] buf)写入 socket 缓冲区,cpu 会参与拷贝

    4. 接下来要向网卡写数据,这项能力 java 又不具备,因此又得从用户态切换至内核态,调用操作系统的写能力,使用 DMA 将 socket 缓冲区的数据写入网卡,不会使用 cpu

    可以看到中间环节较多,java 的 IO 实际不是物理设备级别的读写,而是缓存的复制,底层的真正读写是操作系统来完成的

    • 用户态与内核态的切换发生了 3 次,这个操作比较重量级
    • 数据拷贝了共 4 次

    NIO 优化

    通过 DirectByteBuf

    • ByteBuffer.allocate(10) HeapByteBuffer 使用的还是 java 内存
    • ByteBuffer.allocateDirect(10) DirectByteBuffer 使用的是操作系统内存

    在这里插入图片描述

    大部分步骤与优化前相同,不再赘述。唯有一点:java 可以使用 DirectByteBuf 将堆外内存映射到 jvm 内存中来直接访问使用

    • 这块内存不受 jvm 垃圾回收的影响,因此内存地址固定,有助于 IO 读写
    • java 中的 DirectByteBuf 对象仅维护了此内存的虚引用,内存回收分成两步
      • DirectByteBuf 对象被垃圾回收,将虚引用加入引用队列
      • 通过专门线程访问引用队列,根据虚引用释放堆外内存
    • 减少了一次数据拷贝,用户态与内核态的切换次数没有减少

    进一步优化(底层采用了 linux 2.1 后提供的 sendFile 方法),java 中对应着两个 channel 调用 transferTo/transferFrom 方法拷贝数据

    在这里插入图片描述

    1. java 调用 transferTo 方法后,要从 java 程序的用户态切换至内核态,使用 DMA将数据读入内核缓冲区,不会使用 cpu
    2. 数据从内核缓冲区传输到 socket 缓冲区,cpu 会参与拷贝
    3. 最后使用 DMA 将 socket 缓冲区的数据写入网卡,不会使用 cpu

    可以看到

    • 只发生了一次用户态与内核态的切换
    • 数据拷贝了 3 次

    进一步优化(linux 2.4)

    在这里插入图片描述

    1. java 调用 transferTo 方法后,要从 java 程序的用户态切换至内核态,使用 DMA将数据读入内核缓冲区,不会使用 cpu
    2. 只会将一些 offset 和 length 信息拷入 socket 缓冲区,几乎无消耗
    3. 使用 DMA 将 内核缓冲区的数据写入网卡,不会使用 cpu

    整个过程仅只发生了一次用户态与内核态的切换,数据拷贝了 2 次。所谓的【零拷贝】,并不是真正无拷贝,而是在不会拷贝重复数据到 jvm 内存中,零拷贝的优点有

    • 更少的用户态与内核态的切换
    • 不利用 cpu 计算,减少 cpu 缓存伪共享
    • 零拷贝适合小文件传输

    AIO

    AIO 用来解决数据复制阶段的阻塞问题

    • 同步意味着,在进行读写操作时,线程需要等待结果,还是相当于闲置
    • 异步意味着,在进行读写操作时,线程不必等待结果,而是将来由操作系统来通过回调方式由另外的线程来获得结果

    异步模型需要底层操作系统(Kernel)提供支持

    • Windows 系统通过 IOCP 实现了真正的异步 IO
    • Linux 系统异步 IO 在 2.6 版本引入,但其底层实现还是用多路复用模拟了异步 IO,性能没有优势

    文件 AIO

    先来看看 AsynchronousFileChannel

    @Slf4j
    public class AioDemo1 {
        public static void main(String[] args) throws IOException {
            try{
                AsynchronousFileChannel s = 
                    AsynchronousFileChannel.open(
                    	Paths.get("1.txt"), StandardOpenOption.READ);
                ByteBuffer buffer = ByteBuffer.allocate(2);
                log.debug("begin...");
                s.read(buffer, 0, null, new CompletionHandler<Integer, ByteBuffer>() {
                    @Override
                    public void completed(Integer result, ByteBuffer attachment) {
                        log.debug("read completed...{}", result);
                        buffer.flip();
                        debug(buffer);
                    }
    
                    @Override
                    public void failed(Throwable exc, ByteBuffer attachment) {
                        log.debug("read failed...");
                    }
                });
    
            } catch (IOException e) {
                e.printStackTrace();
            }
            log.debug("do other things...");
            System.in.read();
        }
    }
    

    输出

    13:44:56 [DEBUG] [main] c.i.aio.AioDemo1 - begin...
    13:44:56 [DEBUG] [main] c.i.aio.AioDemo1 - do other things...
    13:44:56 [DEBUG] [Thread-5] c.i.aio.AioDemo1 - read completed...2
             +-------------------------------------------------+
             |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
    +--------+-------------------------------------------------+----------------+
    |00000000| 61 0d                                           |a.              |
    +--------+-------------------------------------------------+----------------+
    

    可以看到

    • 响应文件读取成功的是另一个线程 Thread-5
    • 主线程并没有 IO 操作阻塞

    💡 守护线程

    默认文件 AIO 使用的线程都是守护线程,所以最后要执行 System.in.read() 以避免守护线程意外结束

    网络 AIO

    public class AioServer {
        public static void main(String[] args) throws IOException {
            AsynchronousServerSocketChannel ssc = AsynchronousServerSocketChannel.open();
            ssc.bind(new InetSocketAddress(8080));
            ssc.accept(null, new AcceptHandler(ssc));
            System.in.read();
        }
    
        private static void closeChannel(AsynchronousSocketChannel sc) {
            try {
                System.out.printf("[%s] %s closen", Thread.currentThread().getName(), sc.getRemoteAddress());
                sc.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        private static class ReadHandler implements CompletionHandler<Integer, ByteBuffer> {
            private final AsynchronousSocketChannel sc;
    
            public ReadHandler(AsynchronousSocketChannel sc) {
                this.sc = sc;
            }
    
            @Override
            public void completed(Integer result, ByteBuffer attachment) {
                try {
                    if (result == -1) {
                        closeChannel(sc);
                        return;
                    }
                    System.out.printf("[%s] %s readn", Thread.currentThread().getName(), sc.getRemoteAddress());
                    attachment.flip();
                    System.out.println(Charset.defaultCharset().decode(attachment));
                    attachment.clear();
                    // 处理完第一个 read 时,需要再次调用 read 方法来处理下一个 read 事件
                    sc.read(attachment, attachment, this);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
            @Override
            public void failed(Throwable exc, ByteBuffer attachment) {
                closeChannel(sc);
                exc.printStackTrace();
            }
        }
    
        private static class WriteHandler implements CompletionHandler<Integer, ByteBuffer> {
            private final AsynchronousSocketChannel sc;
    
            private WriteHandler(AsynchronousSocketChannel sc) {
                this.sc = sc;
            }
    
            @Override
            public void completed(Integer result, ByteBuffer attachment) {
                // 如果作为附件的 buffer 还有内容,需要再次 write 写出剩余内容
                if (attachment.hasRemaining()) {
                    sc.write(attachment);
                }
            }
    
            @Override
            public void failed(Throwable exc, ByteBuffer attachment) {
                exc.printStackTrace();
                closeChannel(sc);
            }
        }
    
        private static class AcceptHandler implements CompletionHandler<AsynchronousSocketChannel, Object> {
            private final AsynchronousServerSocketChannel ssc;
    
            public AcceptHandler(AsynchronousServerSocketChannel ssc) {
                this.ssc = ssc;
            }
    
            @Override
            public void completed(AsynchronousSocketChannel sc, Object attachment) {
                try {
                    System.out.printf("[%s] %s connectedn", Thread.currentThread().getName(), sc.getRemoteAddress());
                } catch (IOException e) {
                    e.printStackTrace();
                }
                ByteBuffer buffer = ByteBuffer.allocate(16);
                // 读事件由 ReadHandler 处理
                sc.read(buffer, buffer, new ReadHandler(sc));
                // 写事件由 WriteHandler 处理
                sc.write(Charset.defaultCharset().encode("server hello!"), ByteBuffer.allocate(16), new WriteHandler(sc));
                // 处理完第一个 accpet 时,需要再次调用 accept 方法来处理下一个 accept 事件
                ssc.accept(null, this);
            }
    
            @Override
            public void failed(Throwable exc, Object attachment) {
                exc.printStackTrace();
            }
        }
    }
    
    展开全文
  • Java中五中IO模型详解

    2021-02-24 15:58:53
    在《Unix网络编程》一书中提到了五种IO模型,5种IO模型分别为: 阻塞IO模型、非阻塞IO模型、IO复用模型、信号驱动的IO模型、异步IO模型。下面就分别来介绍一下这5种IO模型的异同。 二、五种IO模型 1、阻塞IO模型...

    一、前言

    在《Unix网络编程》一书中提到了五种IO模型,5种IO模型分别为: 阻塞IO模型、非阻塞IO模型、IO复用模型、信号驱动的IO模型、异步IO模型。下面就分别来介绍一下这5种IO模型的异同。

    二、什么是IO

    IO (Input/Output,输入/输出)即数据的读取(接收)或写入(发送)操作,通常用户进程中的一个完整IO分为两阶段:用户进程空间<–>内核空间、内核空间<–>设备空间(磁盘、网络等)。IO有内存IO、网络IO和磁盘IO三种,通常我们说的IO指的是后两者。网络I/O就是通过网络进行数据的拉取和输出。磁盘I/O主要是对磁盘进行读写工作。

    比如你打开浏览器,访问腾讯首页,浏览器这个程序就需要通过网络IO获取腾讯的网页。浏览器首先会发送数据给腾讯服务器,告诉它我想要首页的HTML,这个动作是往外发数据,叫Output,腾讯服务器把网页发过来,这个动作是从外面接收数据,叫Input。所以,通常,程序完成IO操作会有Input和Output两个数据流。当然也有只用一个的情况,比如,从磁盘读取文件到内存,就只有Input操作,反过来,把数据写到磁盘文件里,就只是一个Output操作。

    流是什么?可以把流想象成一个水管,数据就是水管里的水,但是只能单向流动。Input
    Stream就是数据从外面(磁盘、网络)流进存,Output
    Stream就是数据从内存流到外面去。对于浏览网页来说,浏览器和腾讯服务器之间至少需要建立两根水管,才可以既能发数据,又能收数据。

    在这里插入图片描述

    LINUX中进程无法直接操作I/O设备,其必须通过系统调用请求kernel来协助完成I/O动作;内核会为每个I/O设备维护一个缓冲区。

    对于一个输入操作来说,进程IO系统调用后,内核会先看缓冲区中有没有相应的缓存数据,没有的话再到设备中读取,因为设备IO一般速度较慢,需要等待;内核缓冲区有数据则直接复制到进程空间。

    所以,对于一个网络输入操作通常包括两个不同阶段:

    • 等待网络数据到达网卡→读取到内核缓冲区,数据准备好;
    • 从内核缓冲区复制数据到进程空间

    四、什么是用户空间和内核空间

    虚拟内存被操作系统划分成两块: 内核空间和用户空间。
    为了安全,它们是隔离的,即使用户的程序崩溃了,内核也不受影响。

    内核空间是内核代码运行的地方,
    用户空间是用户程序代码运行的地方。

    当进程运行在内核空间时就处于内核态,当进程运行在用户空间时就处于用户态。

    五 、五种IO模型

    1、阻塞IO模型(BIO)

    这是最传统的一种IO模型,即在读写数据过程中会发生阻塞现象。

    阻塞IO模型: 当用户线程发出IO请求之后,内核会去查看数据是否就绪,如果没有就绪就会等待数据就绪,而用户线程就会处于阻塞状态,用户线程交出CPU。当数据就绪之后,内核会将数据拷贝到用户线程,并返回结果给用户线程,用户线程才解除block状态。

    典型的阻塞IO模型的例子为:

    data = socket.read();
    

    如果数据没有就绪,就会一直阻塞在read方法。

    如图:
    在这里插入图片描述

    2、非阻塞IO模型(NIO)

    非阻塞IO模型: 当用户线程发起一个read操作后,并不需要等待,而是马上就得到了一个结果。如果结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作。一旦内核中的数据准备好了,并且又再次收到了用户线程的请求,那么它马上就将数据拷贝到了用户线程,然后返回。

    所以事实上,在非阻塞IO模型中,用户线程需要不断地询问内核数据是否就绪,也就说非阻塞IO不会交出CPU,而会一直占用CPU。

    典型的非阻塞IO模型一般如下:

    while(true){
       data = socket.read();
       if(data!= error){
           处理数据
           break;
       }
    }
    

    但是对于非阻塞IO就有一个非常严重的问题,在while循环中需要不断地去询问内核数据是否就绪,这样会导致CPU占用率非常高,因此一般情况下很少使用while循环这种方式来读取数据。

    如图:
    在这里插入图片描述

    3、多路复用IO模型(IO Multiplexing)

    多路复用IO模型是目前使用得比较多的模型。Java NIO实际上就是多路复用IO。

    多路复用IO模型: 在多路复用IO模型中,多个进程的IO可以注册到一个复用器(select)上,然后会有一个线程不断去轮询多个socket的状态,只有当socket真正有读写事件时,才真正调用实际的IO读写操作。因为在多路复用IO模型中,只需要使用一个线程就可以管理多个socket,系统不需要建立新的进程或者线程,也不必维护这些线程和进程,并且只有在真正有socket读写事件进行时,才会使用IO资源,所以它大大减少了资源占用。

    在Java 的NIO中,是通过selector.select()去查询每个通道是否有到达事件,如果没有事件,则一直阻塞在那里,因此这种方式会导致用户线程的阻塞。

    也许有朋友会说,我可以采用多线程+ 阻塞IO 达到类似的效果,但是由于在多线程 + 阻塞IO 中,每个socket对应一个线程,这样会造成很大的资源占用,并且尤其是对于长连接来说,线程的资源一直不会释放,如果后面陆续有很多连接的话,就会造成性能上的瓶颈。

    而多路复用IO模式,通过一个线程就可以管理多个socket,只有当socket真正有读写事件发生才会占用资源来进行实际的读写操作。因此,多路复用IO比较适合连接数比较多的情况。

    另外多路复用IO为何比非阻塞IO模型的效率高是因为在非阻塞IO中,不断地询问socket状态时通过用户线程去进行的,而在多路复用IO中,轮询每个socket状态是内核在进行的,这个效率要比用户线程要高的多。

    不过要注意的是,多路复用IO模型是通过轮询的方式来检测是否有事件到达,并且对到达的事件逐一进行响应。因此对于多路复用IO模型来说,一旦事件响应体很大,那么就会导致后续的事件迟迟得不到处理,并且会影响新的事件轮询。

    如图:
    在这里插入图片描述

    4、信号驱动IO模型

    在信号驱动IO模型中,当用户线程发起一个IO请求操作,会给对应的socket注册一个信号函数,然后用户线程会继续执行,当内核数据就绪时会发送一个信号给用户线程,用户线程接收到信号之后,便在信号函数中调用IO读写操作来进行实际的IO请求操作。这个一般用于UDP中,对TCP套接口几乎是没用的,原因是该信号产生得过于频繁,并且该信号的出现并没有告诉我们发生了什么事情

    如图:
    在这里插入图片描述

    5、异步IO模型(AIO)

    异步IO模型才是最理想的IO模型,在异步IO模型中,当用户线程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从内核的角度,当它受到一个asynchronous read之后,它会立刻返回,说明read请求已经成功发起了,因此不会对用户线程产生任何block。然后,内核会等待数据准备完成,然后将数据拷贝到用户线程,当这一切都完成之后,内核会给用户线程发送一个信号,告诉它read操作完成了。也就说用户线程完全不需要关心实际的整个IO操作是如何进行的,只需要先发起一个请求,当接收内核返回的成功信号时表示IO操作已经完成,可以直接去使用数据了。

    也就说在异步IO模型中,IO操作的两个阶段都不会阻塞用户线程,这两个阶段都是由内核自动完成,然后发送一个信号告知用户线程操作已完成。用户线程中不需要再次调用IO函数进行具体的读写。这点是和信号驱动模型有所不同的,在信号驱动模型中,当用户线程接收到信号表示数据已经就绪,然后需要用户线程调用IO函数进行实际的读写操作;而在异步IO模型中,收到信号表示IO操作已经完成,不需要再在用户线程中调用iO函数进行实际的读写操作。

    注意,异步IO是需要操作系统的底层支持,在Java 7中,提供了Asynchronous IO。简称AIO

    前面四种IO模型实际上都属于同步IO,只有最后一种是真正的异步IO,因为无论是多路复用IO还是信号驱动模型,IO操作的第2个阶段都会引起用户线程阻塞,也就是内核进行数据拷贝的过程都会让用户线程阻塞。

    如图:

    在这里插入图片描述

    六、两种高性能IO设计模式

    在传统的网络服务设计模式中,有两种比较经典的模式:

    一种是多线程,一种是线程池。

    对于多线程模式,也就说来了client,服务器就会新建一个线程来处理该client的读写事件,如下图所示:
    在这里插入图片描述

    这种模式虽然处理起来简单方便,但是由于服务器为每个client的连接都采用一个线程去处理,使得资源占用非常大。因此,当连接数量达到上限时,再有用户请求连接,直接会导致资源瓶颈,严重的可能会直接导致服务器崩溃。

    因此,为了解决这种一个线程对应一个客户端模式带来的问题,提出了采用线程池的方式,也就说创建一个固定大小的线程池,来一个客户端,就从线程池取一个空闲线程来处理,当客户端处理完读写操作之后,就交出对线程的占用。因此这样就避免为每一个客户端都要创建线程带来的资源浪费,使得线程可以重用。

    但是线程池也有它的弊端,如果连接大多是长连接,因此可能会导致在一段时间内,线程池中的线程都被占用,那么当再有用户请求连接时,由于没有可用的空闲线程来处理,就会导致客户端连接失败,从而影响用户体验。因此,线程池比较适合大量的短连接应用。

    因此便出现了下面的两种高性能IO设计模式:Reactor和Proactor。

    类别开发模式
    BIOThread-Per-Connection
    NIOReactor
    AIOProactor
    • Reactor: 在Reactor模式中,会先对每个client注册感兴趣的事件,然后有一个线程专门去轮询每个client是否有事件发生,当有事件发生时,便顺序处理每个事件,当所有事件处理完之后,便再转去继续轮询。
      多路复用IO就是采用Reactor模式,为了提高事件处理速度,可以通过多线程或者线程池的方式来处理事件。Java NIO使用的就是这种。

    • Proactor: 在Proactor模式中,当检测到有事件发生时,会新起一个异步操作,然后交由内核线程去处理,当内核线程完成IO操作之后,发送一个通知告知操作已完成,可以得知,异步IO模型采用的就是Proactor模式。Java AIO使用的这种

    如下图所示:
    在这里插入图片描述

    从这里可以看出,上面的五种IO模型中的多路复用IO就是采用Reactor模式。注意,上面的图中展示的 是顺序处理每个事件,当然为了提高事件处理速度,可以通过多线程或者线程池的方式来处理事件。Java NIO使用的就是这种

    在Proactor模式中,当检测到有事件发生时,会新起一个异步操作,然后交由内核线程去处理,当内核线程完成IO操作之后,发送一个通知告知操作已完成,可以得知,异步IO模型采用的就是Proactor模式。Java AIO使用的这种。

    七、参考文章

    1. https://blog.csdn.net/qq_34515959/article/details/108991230
    2. https://blog.csdn.net/weixin_45775963/article/details/104539820
    3. https://blog.csdn.net/zzti_erlie/article/details/109143698
    展开全文
  • udp多路IO模型.zip

    2019-12-21 19:57:22
    select,poll,epoll都是IO多路复用的机制,可以监测多个描述符,当某个或多个描述就绪,能够通知程序进行相应的读写行为。本demon是基于linux下udp通信实现,源码包括select、poll、epoll模型的udp服务端代码。
  • Java基础之IO模型

    2022-03-21 14:03:48
    Java基础之IO模型 I/O 常见的IO模型 Java中3种常见的IO模型 BIO(Blocking I/O) NIO(Non-blocking/New I/O) AIO(Asynchronous I/O) 总结(BIO,BIO,AIO有什么区别) 经典例子解析理解 I/O I/O(input / output)...

    I/O

    I/O(input / output) 即 输入/输出

    从计算机结构的角度来解读I/O。
    根据冯·诺伊曼,计算机结构分为5大部分:运算器、控制器、存储器、输入设备、输出设备。

    在这里插入图片描述
    输入设备(比如键盘)和输出设备(比如显示器)都输入外部设备。网卡、硬盘这种即可以作为输入设备也可以作为输出设备。
    输入设备向计算机输入数据,输出设备接收计算机输出的数据。

    从计算机结构的角度来看,I/O描述了计算机系统与外部设备之间的通信过程。

    再从应用程序的角度来看I/O。
    为了保证操作系统的稳定性和安全性,一个进程的地址空间划分为用户空间(User space)内核空间(Kernel space)。并且用户空间的程序不能直接访问内核空间。


    我们平时运行的应用程序都运行再用户空间,只有内核空间才能运行系统态级别的资源有关的操作,比如如文件管理、进程通信、内存管理等。就是说,我们要想进行IO操作,一定要依赖内核空间能力

    当要想执行IO操作,由于没有执行这些操作的权限,只能发起系统用请求操作系统帮忙完成。所以,用户进程想要执行IO操作的话,必须通过系统调用来间接访问内核空间。

    我们平时接触最多的就是磁盘IO(读写文件)网络IO(网络请求和响应)
    从应用程序的角度来看,应用程序对操作系统的内核发起IO调用(系统调用),操作系统负责的内核执行具体的IO操作。就是说,应用程序实际只是发起了IO操作的调用而已,具体IO的执行是由操作系统的内核来完成的。

    当应用程序发起I/O调用后,会发生两个步骤:

    1. 内核等待I/O设备准备好数据
    2. 内核将数据从内核空间拷贝到用户空间。

    常见的IO模型

    UNIX系统下,IO模型一共有5种:同步阻塞I/O、同步非阻塞I/O,I/O多路复用、信号驱动I/O和异步I/O。这也是常用的5种IO模型。

    Java中3种常见的IO模型

    BIO(Blocking I/O)

    BIO属于同步阻塞I/O模型

    同步阻塞IO模型,应用程序发起read调用后,会一直阻塞,直到在内核把数据拷贝到用户空间。
    同步阻塞
    在客户端连接数量不高的情况下,没有问题,但,当面对十万甚至百万级别连接的时候,传统的BIO模型就无能为力了,所以,需要用更高效的I/O处理模型来应对更高的并发量。

    NIO(Non-blocking/New I/O)

    Java中的NIO是在Java 1.4引入,对应java.io包,提供了ChannelSelector,Buffer等抽象。NIO中的N理解为Non-blocking,不单纯是New,
    它支持面向缓冲,基于通道的I/O操作方法,对于高负载、高并发的网络与应用,应使用NIO。
    Java中的NIO可以看作为是I/O多路由复用。也有人认为,Java中的NIO属于同步非阻塞IO模型。
    同步非阻塞模型
    同步非阻塞
    同步非阻塞IO模型中,应用程序会一直read调用,等待数据从内核空间拷贝到用户空间的这段时间里,线程依然是阻塞的,直到在内核把数据拷贝到用户空间。
    相比于同步阻塞IO模型,同步非阻塞IO模型确实有了很大改进。通过轮询操作,避免了一直阻塞。
    但也同样存在着问题:应用程序不断进行I/O系统调用轮询数据是否已经准备好的过程十分耗费CPU资源

    这个时候,I/O多路由复用模型就登场了。
    多路由复用
    IO多路复用模型中,线程首先发起select调用,询问内核数据是否准备就绪,等内核数据准备好了,用户线程再发起read调用。read调用的过程(数据从内核空间->用户空间)还是阻塞的。

    支持IO多路复用的系统调用,有select,epoll… seclet系统调用,是目前几乎在所有的操作系统上都支持。

    • select调用: 内核提供的系统调用,支持依次查询多个系统调用的可用状态,几乎所有的操作系统都支持。
    • epoll调用: linux2.6内核,属于select调用的增强版,优化了IO的执行效率。

    IO多复用模型,通过减少无效的系统调用,减少了对CPU资源的消耗。

    java中的NIO,有一个非常重要的选择器(Selector),也叫 多路复用器。 通过它,只要一个线程便可管理多个客户端连接,当客户端数据到了后,才会为其服务。
    选择器

    AIO(Asynchronous I/O)

    AIO也就是NIO 2。Java 7 中引入了NIO的改进版,它是异步IO模型。

    异步IO是基于事件回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。
    异步
    至今来说AIO的应用还不广泛,Netty也尝试使用AIO,不过放弃了,原因是:使用AIO后,在Linux上的性能并没有提升多少。

    总结(BIO,BIO,AIO有什么区别)

    • BIO(Blocking I/O):服务器实现模式为一个连接一个线程,客户端有连接请求时服务器就要启动一个线程进行处理,若这个连接不做任何操作还造成不必要的线程开销,可以通过线程池机制来改善。BIO方式适合连接数目小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,在JDK1.4以前时唯一的IO。
    • NIO(NEW I/O):服务器实现模式为一个请求一个线程,客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有IO请求时才启动一个线程进行处理,NIO方式适合用于连接数目多且连接时间短(轻操作)的架构,如聊天服务器,并发局限于应用中,编程复杂,JDK 1.4之后开始支持。
    • AIO(Asynchronous I/O):服务器实现模式为一个有效请求一个线程,客户端的IO请求都是由操作系统先完成再通知服务器用其启动线程进处理,AIO适合用于连接数目多且连接比较长(重操作)的架构,如相册服务器,充分调用OS参与并发操作,编程复杂,JDK 1.7开始支持。

    经典例子解析理解

    BIO: 来到厨房,开始烧水NIO,并坐在水壶面前一直等待水烧开。
    NIO: 来到厨房,开AIO烧水,但不坐在水壶面前一直等,而去做其他事,然后每隔几分钟到厨房看一下有没有烧开。
    AIO: 来到厨房,开始烧水,不一直等待水烧开,而是坐在水壶上面装个开关,水烧开之后它会通知我们。

    展开全文
  • 在jdk1.4之前JAVA只有同步阻塞IO,在jdk1.4版本更新同步非阻塞的NIO,在jdk1.7又更新了异步的NIO2.0,本文将简单介绍下Linux有哪些网络IO模型,以及JAVA中的IO所对应的网络IO模型。 理解本文之前需要先知道一个概念...
  • 同步、异步、阻塞、非阻塞,当这些网络IO名词堆到一起时难免使编程初学者感到困惑,这里我们就来为大家总结网络IO模型与select模型的Python实例讲解:
  • windows网络socket模型 异步通信模型 select模型 异步选择 异步事件 重叠IO 完成端口 详细介绍了区别 用法与实例
  • 网络模型——四种常见网络IO模型

    千次阅读 2021-03-13 19:26:41
    四种主要的IO模型2.1 基本概念2.1.1 阻塞与非阻塞2.1.2 同步与异步2.1.3 四种IO模型2.2 同步阻塞IO(Blocking IO) 网络IO模型一共介绍以下四种: 同步阻塞IO、同步非阻塞IO、IO多路复用和异步IO。 1.IO读写原理 文件的...
  • Nginx的IO模型详解

    千次阅读 2019-10-25 16:30:46
    在Nginx和Redis这两大中间件中,都是使用了IO多路复用的IO模型,其中 Redis是经典的单线程模型,非阻塞的io,redis使用epoll作为io多路复用技术的实现,在加上Redis自身的事件处理模型将epoll中的链接、读写、关闭都...
  • TCP服务器IO模型之非阻塞轮询,使用内核链表,单进程实现
  • 聊聊Linux 五种IO模型 5种IO模型、阻塞IO和非阻塞IO、同步IO和异步IO 看了一些文章,发现有很多不同的理解,可能是因为大家入切的角度、环境不一样。所以,我们先说明基本的IO操作及环境。 本文是在《UNIX网络编程 ...
  • 常见IO模型详解

    千次阅读 2019-02-14 16:18:27
    服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种: 同步阻塞IO(Blocking IO):即传统的IO模型。 同步非阻塞IO(Non-blocking IO):默认创建的socket都是阻塞的,非阻塞IO要求socket被设置为...
  • 浅析IO,NIO和五种IO模型

    千次阅读 2019-11-15 13:33:34
    浅析IO,NIO和五种IO模型 文章目录浅析IO,NIO和五种IO模型一:几个基本的概念二:同步,异步,阻塞,非阻塞1.同步:2.异步:3.阻塞:4.非阻塞:注意:三:阻塞IO,非阻塞IO,同步IO,异步IO1.阻塞IO:2.非阻塞式IO:4....
  • 五种基本IO模型介绍

    千次阅读 2021-12-15 17:36:14
    一、阻塞式IO 大家最开始接触网络编程时,基本都是接触的阻塞式IO,在一个基本的TCP服务器程序中,我们使用recv()函数来...我们以图表的形式来描述阻塞式IO模型: 二、非阻塞式IO 在介绍了阻塞IO后,我们不免会对非
  • IOCP模型作为高性能IO模型,线程本地内存池,文件缓存,分析模块。 ## 进度 1. IOCP模型: [x] AcceptEx接收数据 [x] DisconnectEx复用Sokcet [x] 消息循环 2. 内存池 [x] 16Bytes * [1-32] 的小块内存分配 ...
  • 5种IO模型

    千次阅读 2020-06-05 19:40:52
    目录一、IO模型1、什么是IO2、IO的分类3、网络输入操作的两个阶段4、用户空间和内核空间5、同步和异步6、阻塞和非阻塞二、阻塞IO模型三、非阻塞IO模型四、IO复用模型五、信号驱动IO模型六、异步IO模型七、5种IO模型...
  • Windows下6种IO模型

    2021-04-11 13:04:44
    IO模型2. 阻塞(blocking)IO和非阻塞(non-blocking)3. 同步(synchronous)IO和异步(asynchronous)4. 6种I/O模型 1. IO模型 对于一个network IO(这里我们以read举例),它会涉及到两个系统对象,一个是调用这...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 367,211
精华内容 146,884
关键字:

io模型