• Home
  • 写文
  • 关于
    • jlweb Blog photo

      jlweb Blog

      occupied with moon theme of jelly

    • 详情
    • Github
    • Steam
  • 文章
    • 所有文章
    • 所有标签
  • 项目
  • 主站
search clear

I_O多路复用

10 Dec 2023

阅读时长 ~1 分钟

编辑
1.前世今生

访问管道或 socket ,默认是阻塞 I/O,当我们改用非阻塞 I/O 的方式访问时候,由于非阻塞其实是单独开辟线程轮询判断I/O是否完成。

在没有事件通知机制的情况下,采用多线程实现非阻塞I/O的方式是通过轮询来实现。线程会不断地轮询等待的I/O操作,而不会被挂起。 具体来说,如果一个I/O操作还未完成,线程不会被阻塞,而是立即返回,继续执行后续的轮询查询。这种方式被称为”非阻塞轮询模型”,在没有更高效的事件通知机制时,是一种常见的非阻塞I/O实现方式。

※易混淆: 这里的阻塞非阻塞概念是针对程序I/O操作的(即在3里提到的第一步”数据准备的过程”),而对于操作系统层面的I/O复用函数,如epoll、select、poll之类,它们实际上

2.select、poll (非阻塞 I/O 的Plus版本)

于是 I/O 多路复用技术就出来了,如 select、poll,它是通过 I/O 事件分发,当内核数据准备好时,再以事件通知应用程序进行操作。它是通过 I/O 事件分发,当内核数据准备好时,再以事件通知应用程序进行操作。

3.异步I/O与同步I/O区别

区别是异步使用了aio_read()获取数据,而同步是read().

当我们发起 aio_read 之后,就立即返回,内核自动将数据从内核空间拷贝到应用程序空间,这个拷贝过程同样是异步的

按照I/O完整流程,分为两步:

  1. 数据准备的过程
  2. 数据从内核空间拷贝到用户进程缓冲区的过程
    • 阻塞I/O:阻塞I/O会阻塞在”过程1”(数据准备的过程)和”过程2”(数据从内核空间拷贝到用户进程缓冲区的过程)。在阻塞I/O的执行过程中,线程会等待I/O操作的完成,直到数据准备和数据拷贝都完成,然后才能继续执行。
    • 非阻塞I/O:非阻塞I/O会阻塞在”过程2”(数据从内核空间拷贝到用户进程缓冲区的过程)。线程在发起非阻塞I/O后会立即返回,不会等待I/O操作的完成,但当数据准备就绪后,线程在数据拷贝的过程中可能会阻塞。
    • 基于非阻塞I/O的多路复用:基于非阻塞I/O的多路复用会阻塞在”过程2”(数据从内核空间拷贝到用户进程缓冲区的过程)。它使用I/O多路复用机制(如select、poll、epoll等)来同时监控多个I/O事件,在有I/O事件就绪时返回就绪的I/O描述符,然后线程需要在数据拷贝的过程中阻塞。
    • 异步I/O:异步I/O不会阻塞在”过程1”和”过程2”中的任何一个。当应用程序发起异步I/O操作后,线程会立即返回,不会等待I/O操作的完成。在I/O操作的整个执行过程中,线程不需要等待,而是通过事件通知机制在I/O操作完成后得到通知,然后处理I/O操作的结果。
    • 信号驱动I/O:第五大I/O模型,和I/O多路复用区别在于,信号驱动I/O是给fd注册信号,并自己处理信号,而I/O多路复用是通过内核提供函数监听是否就绪,其信号不需要由我们处理,我们只需要在事件循环中查询fd状态?

因此,异步I/O和同步I/O的最本质的区别在于线程是否会在I/O操作的过程中被阻塞。异步I/O不会阻塞线程,而同步I/O会在不同程度上阻塞线程,取决于具体的同步I/O实现方式。

4.既然异步I/O这么好,为什么不都用异步I/O

场景1:大量I/O操作,但是I/O数据量很小 这种适合同步非阻塞 I/O 的Plus版本,因为数据拷贝过程时间几乎忽略不计,数据拷贝到用户态的这个阻塞时间带来的影响便可以忽略,这样的好处就是,当我们编程维护适合更加简单

场景2:少量I/O,少量数据I/O 低级程序应付的大多是这种场景,用于要求不高的功能实现起来较快,常见的操作是: a.单线程阻塞I/O(死等数据回传+delay超时检测)、b.多线程非阻塞(每次I/O就开线程+死等+delay控制)

场景3:大量I/O操作,数据量巨大 这种就适合异步I/O,因为同步I/O已经无法解决数据拷贝过程的阻塞了,会导致数据read()过程把程序卡死,无法执行其他重要功能。

5.windows下高并发网络开发所使用的I/O复用函数

Windows也提供了类似的I/O多路复用机制,虽然与Linux上的select、poll和epoll有一些差异,但它们在功能上是类似的。

  1. select:
    • Windows提供了select函数,与Linux上的select功能类似,用于在多个I/O描述符上进行多路复用,并在有事件就绪时通知应用程序。但在Windows上,select使用了fd_set数据结构来保存待监视的I/O描述符,而不是位图。另外,Windows上的select存在一些性能限制,不太适合处理大量文件描述符的情况。
  2. poll:
    • Windows不直接提供poll函数。如果需要使用类似于poll的功能,可以通过使用第三方库或者自己实现。
  3. IOCP(Input/Output Completion Ports):
    • 在Windows上,IOCP是一种高效的I/O多路复用机制,相当于Linux上的epoll。
    • IOCP采用事件驱动的方式,可以在一个线程内同时监视多个I/O事件,并在事件就绪时通知应用程序。它适用于处理大量连接和高并发的网络应用。
    • 使用IOCP需要调用一系列的函数来创建IOCP对象、关联I/O操作,然后通过GetQueuedCompletionStatus或GetQueuedCompletionStatusEx等函数等待I/O完成事件。

总结: Windows提供了类似于Linux上select、poll和epoll的I/O多路复用机制。select在Windows上存在一些性能限制,IOCP是Windows下更为推荐和高效的I/O多路复用机制,特别适用于处理大规模高并发的网络应用。如果需要处理大量连接和高并发,IOCP是更好的选择。



🥁-Network Share Tweet +1