2.3.7 非阻塞IO中需要关注的问题

如何处理非阻塞IO中的 short write

一般来说在非阻塞编程中:

  • 对于非阻塞的读,如果读数据不全,我们需要将数据缓存,等凑够一条完整消息再触发消息处理逻辑。
  • 对于非阻塞的写,通常是网络库需要实现的功能,我们需要做的是告诉网络库我们需要发送多少数据,至于网络库内部如何处理事件我们在编程阶段不关心。

以发数据为例:

  • 如果数据发送不完整,剩下的数据需要放置到一个发送的缓冲区中。
    • 如果缓冲区非空,则我们不能对新数据write。
      因为这会造成数据乱序。只有等上一条消息全部发送成功后才可以对新消息进行发送。
      方案一:先尝试发送一次数据,如果数据发送不完整,将剩余数据存放在发送缓冲区(应用层的),然后注册POLLOUT事件用于处理剩余的数据发送。
    • 或者始终从缓冲区发送数据。
      方案二:将所有数据都存放在发送缓冲区,然后再去注册POLLOUT事件,只通过POLLOUT事件处理数据的发送。
  • 向套接字中写数据,关注POLLOUT事件。
    • 当POLLOUT准备好时,开始从发送缓冲区(应用层)中取数据向sock中写
    • 当发送换缓冲区(应用层)为空时,停止关注POLLOUT事件。
      注意,如果忘记取消关注POLLOUT事件,则认为sock一直可写(LT模式持续通知我们有事件),而实际上我们并没有数据向sock写,会进入一种busy loop状态,大量空耗cup过度占用资源。

如何处理非阻塞IO中对方接受缓慢。

设想,本端在发送一个文件时,将文件加载到内存中然后通过网络向对端发送,而如果对方接受缓慢,那本端的发送方就要迁就对方从而缓慢发送,但是本端内存中缓存的数据就会持续占用在内存中。

如果待发送的数据很多的话,那么一味的将文件读取到内存中。等待向对端发送显然是不明智的。因为这将会占用大量的内存资源。

这种场景类似于水池灌溉模型,即一个水池一边放水一边注水。放水的下流出口用于灌溉农田,注水的上流入口取自水库抽取的水源。假设现在要达到灌溉最大化,既不浪费水源,又能以最大速率灌溉农田。那么由于 V 注 水 V_{注水} V!= V 放 水 V_{放水} V 必然会产生下面这两种结果:

  • 如果注水量大于放水量,那么一段时间后池子将会溢出(浪费水源)。
  • 如果注水量小于放水量,那么一段时间后池子内将不会存留下水(灌溉效率没有达到最大化)。

.
最好的状态是池子内永远有一定量的水位,这样从池子内流出的水将以最大速率流出灌溉农田,同时也不会浪费水源。
.
那么解决方案就是认为设定一个最高/低水位(hight/low water mark),如果水池内的水位高于最高水位则停止注水,如果水位低于最低水位则开始注水。这样使得水位在 hwm ~ lwm 之间浮动,而水池出水率始终是最大值。

同理,我们可以参考高低水位的方式去设计。例如在向对端发送数据时,内存中缓冲的数据已经高出规定阈值时,我们可以考虑不去读对方的下一个请求直到本次发送完成,并且可以限制从本地读取待发送文件的速率。

但这终究不是一个完美的解决方案,对于接收端大量频繁的请求而言,我们不 read() 这些请求并不是一个好的解决方案,最好的方式是接收两方进行协议层面的商定,通过滑动窗口的思想告知接收端是否可以开启下一次的请求。这样方可避免由于接收方大量的数据请求而造成发送端发缓冲区数据的大量堆积(比如,接收端每次get image请求,发送端将对应image数据发送给接收端。对于接收端而言发送一次请求耗费的数据量很少,而发送发要回应的每个请求的数据量等很大,如果发送方一次接收到多个请求则这些应答数据将会占满发送发的缓冲区)。

使用 LT(level trigger) 模式还是 ET(edge trigger) 模式

  • select() 与 poll() 都是 LT 模式。 如果有事件到达,还没有进行处理,则它会一直通知,直到事件被处理。
  • epoll() 即 edge-poll。 它同时支持 LT 模式与 ET 模式。
  • 能否结合两种模式的特点,分别在不同的场景使用不同的模式
    • 对于ET模式而言, 更适用于write事件和accept事件(accept如果文件描述符用完,会陷入死循环中,因此使用模式更好)
    • LT模式 更适用于read()事件,它不会造成接收的饥饿,ET模式可能会造成数据接收不完整的情况。
    • 可惜的是,目前内核中使用同一种数据结构表示读和写事件,读写事件放在一个就绪列表中,在读出后才判断是读事件还是写事件。因此,我们无法实现在不同的场景使用不同的模式。 值得注意的是,许多第三方网络库都使用的是LT模式,一般来说为了互相的兼容推荐使用LT模式。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我叫RT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值