阻塞非阻塞、同步异步、reactor和proactor详解

(Linux网络编程笔记)阻塞非阻塞、同步异步、reactorproactor

阻塞与非阻塞

在调用一个系统调用时,该系统调用不会立即返回,而是当还未准备就绪时,会被阻塞,也就是阻塞到有结果再返回。

例如在阻塞socket中调用recv(),如果缓冲区中没有数据,则会一直阻塞等待有数据为止。

在这里插入图片描述

而非阻塞调用则会立即返回,如果该调用的数据未准备好,则会返回错误,如EWOULDBLOCK。假设调用非阻塞socketrecvfrom(),此时则会立即返回,若无数据,返回EWOULDBLOCK。此时需要多次调用recvfrom()

即阻塞非阻塞都是针对于发起操作时,检查是否就绪阶段。

在这里插入图片描述

同步、异步

同步异步都是针对于真正发起操作之后的行为。

同步是指要等待I/O操作完毕,当数据就绪后,也就是有数据可读时,要将process阻塞,一直到读完为止。

而对于异步,用户发起操作之后,可以立即去处理其他操作。全程交给内核处理。

即同步异步针对于数据操作阶段,与阻塞非阻塞无关。

在这里插入图片描述

I/O Model对比

阻塞I/O

就是最一般的I/O,在读取、写入时要阻塞等待。

不太恰当的例子
A在钓鱼,用的是最原始的鱼竿,必须一直守着等待鱼上钩再拉杆。
非阻塞I/O

我们把一个SOCKET接口设置为非阻塞就是告诉内核,当所请求的I/O操作无法完成时,不要将进程睡眠,而是返回一个错误。这样我们的I/O操作函数将不断的测试数据是否已经准备好,如果没有准备好,继续测试,直到数据准备好为止。在这个不断测试的过程中,会大量的占用CPU的时间。

不太恰当的例子
B在钓鱼,用的是一种相对先进的鱼竿,上面会显示鱼有没有上钩,因此每查看一次就能立即知道是否上钩(对应于调用立即能有结果)。但是要不断循环检查。
I/O复用

典型的有selectepoll

I/O复用本身是阻塞的。也就是说,I/O复用函数调用实际在发起操作时也需要阻塞等待就绪,只不过I/O复用能够处理多个socket。

不太恰当的例子
B用的跟A一样的鱼竿,只是B觉得A太慢了,第一种情况,B买了很多鱼竿,并且雇佣了工人不停地轮询他的鱼竿(select/poll),一旦有上钩,就在鱼竿上做个标记,于是B就需要轮询一边看哪个鱼竿上钩。第二种,就如同在每一根鱼竿上安装了闹钟(epoll),一旦上钩,就会通知B某个鱼竿上钩了。
信号驱动I/O(SIGIO)

首先我们允许套接口进行信号驱动I/O,并安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据

不太恰当的例子
这里也相当于在鱼竿安装了闹钟,但区别于epoll可能在于不能管理多个鱼竿。
以上都属于同步I/O,在数据拷贝时,都会被阻塞。也就是说,鱼上钩之后,拉上来的过程要本人去拉,这可以看作阻塞,因为渔者不能干别的事情。
异步I/O

用户进程发起read操作之后,立刻就可以开始去做其它的事。全程不需要关心,都交给内核完成。

不太恰当的例子
C是个大老板,他只要跟他佣人说,我要钓鱼。剩下的活全由佣人完成,C只需要去拿鱼就行。

在这里插入图片描述

Reactor、Proactor

在高性能的I/O设计中,有两个比较著名的模式Reactor和Proactor模式,其中Reactor模式用于同步I/O,而Proactor运用于异步I/O操作。

搞清楚了以上概念以后,我们再回过头来看看,Reactor模式和Proactor模式。

首先来看看Reactor模式,Reactor模式应用于同步I/O的场景。我们以读操作为例来看看Reactor中的具体步骤:

读取操作:

  1. 应用程序注册读就需事件和相关联的事件处理器

  2. 事件分离器等待事件的发生

  3. 当发生读就需事件的时候,事件分离器调用第一步注册的事件处理器

  4. 事件处理器首先执行实际的读取操作,然后根据读取到的内容进行进一步的处理

下面我们来看看Proactor模式中读取操作和写入操作的过程:

读取操作:

  1. 应用程序初始化一个异步读取操作,然后注册相应的事件处理器,此时事件处理器不关注读取就绪事件,而是关注读取完成事件,这是区别于Reactor的关键。

  2. 事件分离器等待读取操作完成事件

  3. 在事件分离器等待读取操作完成的时候,操作系统调用内核线程完成读取操作,并将读取的内容放入用户传递过来的缓存区中。这也是区别于Reactor的一点,Proactor中,应用程序需要传递缓存区。

  4. 事件分离器捕获到读取完成事件后,激活应用程序注册的事件处理器,事件处理器直接从缓存区读取数据,而不需要进行实际的读取操作。

Proactor中写入操作和读取操作,只不过感兴趣的事件是写入完成事件。

从上面可以看出,Reactor和Proactor模式的主要区别就是真正的读取和写入操作是有谁来完成的,Reactor中需要应用程序自己读取或者写入数据,而Proactor模式中,应用程序不需要进行实际的读写过程,它只需要从缓存区读取或者写入即可,操作系统会读取缓存区或者写入缓存区到真正的IO设备.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值