> 文章列表 > C++ :websocket 通讯下的五种 I/O 模型

C++ :websocket 通讯下的五种 I/O 模型

C++ :websocket 通讯下的五种 I/O 模型

目录

I/O 多路复用(一种同步 I/O 模型

非阻塞与阻塞

select、poll、epoll

起因

改善

select 与 poll 的差别

I/O 模型

阻塞 I/O 模型

非阻塞 I/O 模型

I/O 多路复用模型

信号驱动 I/O 模型(SIGIO)

异步 I/O 模型(AIO)


I/O 多路复用(一种同步 I/O 模型)

多路(文件句柄)监听+阻塞/非阻塞I/O

一个线程完成多个网络连接(一对多);

一旦某个文件句柄就绪,就能够通知应用程序进行响应读写操作;

没有文件句柄则会阻塞应用程序,交出 CPU;

select、poll、epoll(Linux 最先进的方式)是目前主流的多路复用I/O技术。

非阻塞与阻塞

  • 非阻塞:通过不断的数据检测直到出现数据响应(事件请求)。

  • 阻塞:等待数据响应(事件请求),期间不进行其他处理,直到响应发现;

select、poll、epoll

起因

对于非阻塞,不断检测流来判定事件处理,是非法的,当所有的流都没有 I/O 事件时,非阻塞将会不断运行并浪费 CPU 时间片,从而产生资源浪费,由此通过引进中间层(代理:select、poll)来对流检测进行事件处理(仅仅处理事件检测),如果没有则进行阻塞,由此避免了线程的不断轮询。

改善

对于 select,通过中间层的处理,我们完善了轮询的阻塞,但是并无法知道当前 select 进行的处理事件数量有多少,当假设有全部流事件存在,select 有 O(n) 的无差别轮询复杂度,处理的流越多,无差别轮询时间越长。

不同于忙轮询和无差别轮询,epoll 会把哪个流发生了怎样的 I/O 事件通知我们,属于事件驱动,通过明确指定事件,便可以定位流,从而不再进行轮询操作,而是明确指定对应的流事件处理。

I/O 模型

阻塞 I/O 模型

  • 默认情况下,所有文件操作都是阻塞的;

  • 第一阶段:应用层数据传到 kernel;第二阶段:kernel 复制到 user space

  • 两个阶段一起阻塞,阻塞状态下,程序不会浪费 CPU;

  • recvfrom 只有等到 kernel 中的数据复制到用户进程缓冲区之后才返回并解除阻塞。

非阻塞 I/O 模型

  • 将套接口设置为非阻塞模式,就是在告诉内核,当请求的 I/O 操作后立刻返回,不可用则返回错误。

  • 当数据没有准备好时,内核立即返回 EWOULDBLOCK 错误,第四次调用系统调用时,数据已经存在,这时将数据复制到进程缓冲区中。其中有:不断轮询(polling),类似频繁等待

  • 第一阶段非阻塞,第二阶段必须阻塞

I/O 多路复用模型

  • 由于以上阻塞与非阻塞都会导致接受 fd 的时候无法跳出等待或者阻塞环节,因此在避免多线程启动导致大量浪费资源的条件下,出现了一个进程处理多个 fd 请求的 io 多路复用技术;

  • 此模型用到 select 和 poll 函数,这两个函数也会使进程阻塞;

  • select 先阻塞,有活动套接字才返回,但和阻塞 I/O 不同的是,两个函数可以同时阻塞多个 I/O 操作,而且可以同时对多个读、写操作的 I/O 函数进行检测,直到有数据可读或可写;

  • select被调用后,进程会被阻塞,内核监视所有 select 负责的 socket,当至少有一个 socket的数据准备好(I/O 可用)时,返回可读,由 recvfrom 处理数据。

信号驱动 I/O 模型(SIGIO)

  • 首先我们允许套接口进行信号驱动 I/O ,并安装一个信号处理函数,进程继续运行并不阻塞

  • 数据准备好时,进程会收到一个 SIGIO 信号,可以在信号处理函数中调用 I/O 操作函数处理数据;

  • 数据报准备好读取时,内核就为该进程产生一个 SIGIO 信号

  • 随后既可以在信号处理函数中调用 recvfrom 读取数据报,并通知主循环数据已准备好待处理,也可以立即通知主循环,让它来读取数据报;

  • 优势在于等待数据报到达第一阶段期间,进程可以继续执行,不被阻塞。免去了 select 的阻塞与轮询,当有活跃套接字时,由注册的 handler 处理;

 

异步 I/O 模型(AIO)

  • 进程发起 read 操作之后,立刻就可以开始去做其它的事。而另一方面,从 kernel 的角度,当收到一个 asynchronous read 之后,会立刻返回,不会对用户进程产生任何 block

  • 然后,kernel 会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel 会给用户进程发送一个 signal,通知 read 操作完成

工作机制:告诉内核启动某个操作,并让内核在整个操作 (包括第二阶段,即将数据从内核拷贝到用户层进程缓冲区中) 完成后通知我们

  • 与 SIGIO 区别在于:信号驱动 I/O 模型是由内核通知我们何时可以启动一个 I/O 操作,而异步 I/O 模型是由内核通知我们 I/O 操作何时完成
  1. 优点:整个过程都不阻塞,一步到位;非常使用高并发应用;
  2. 缺点:模型复杂,实现、开发难度较大。

Free Fonts