【问题标题】:Is there any difference between blocking and non blocking send阻塞和非阻塞发送有什么区别吗
【发布时间】:2012-12-04 17:49:31
【问题描述】:

如果应用程序可以确保套接字的发送缓冲区中始终有空间,那么阻塞和非阻塞发送是否具有相同的性能?在这种情况下,这两种方法比另一种方法有什么优势吗?

【问题讨论】:

  • 你为什么不同时实现这两个,并运行一些测试?
  • 你为什么要这样做?系统已经为您提供了一种为您完成工作的方式。
  • @xiaoyi - 内核中有很多很多代码路径,简单的测试程序可能无法执行。这是一个有效的问题,“试一试”不是有用的回答。
  • @Nemo 但如果它那么复杂,如何回答这个问题?
  • @xiaoyi 问题是关于“优势”,而不是性能。还有其他优点。

标签: c linux tcp


【解决方案1】:

阻塞和非阻塞send的唯一区别是内核是让你的进程进入睡眠状态还是返回EWOULDBLOCK。所以就性能而言,应该没有区别。

但是,我怀疑您的隐含假设,即发送不能仅仅因为发送缓冲区有可用空间而阻塞。想象一下系统上的一个空闲套接字,它对内存提出了很高的要求。我不一定希望内核为您的发送缓冲区“固定”物理页面;我希望它会将该内存用于有用的东西。然后当你尝试发送时,内核需要为发送缓冲区获取一个空闲页面;如果没有这样的页面可用,它可能会决定返回EWOULDBLOCK 而不是等待(比如说)交换。

现在,有很多“可能”和“可能”,更熟悉内核内部的人可能会告诉我我错了。但即使 Linux 今天不这样,明天也可能会这样。您是否 100% 确定您永远不会在 Linux 以外的任何平台上运行您的应用程序?

所以我不会以如此脆弱的假设来编写我的应用程序。我建议你决定阻塞还是非阻塞语义对你自己的代码更有意义,不要试图玩弄内核的内部结构。

[更新]

我希望我不必深入研究 Linux 内部,但过度自信的反对者驱使我这样做。

net/ipv4/tcp.c at the "new_segment" label开头:

new_segment:
if (!sk_stream_memory_free(sk))
    goto wait_for_sndbuf;

skb = sk_stream_alloc_skb(sk, 0, sk->sk_allocation);
if (!skb)
    goto wait_for_memory;

看看“wait_for_sndbuf”与“wait_for_memory”有何不同?这就是我要说的。

在“wait_for_memory”标签处,有一个对sk_stream_wait_memory 的调用,其值为timeo,取决于这是否是非阻塞发送。反过来,该函数要么使进程进入睡眠状态,要么返回 EAGAIN,具体取决于 timeo

[更新 2]

只是为了清楚我在回答什么问题......

我将这个问题解释为,“如果我知道我的套接字的发送缓冲区有足够的可用空间,那么该套接字上的阻塞和非阻塞 send 之间是否存在任何差异(性能或其他方面)? "

前提当然是可能的,例如,如果您的协议是发送一条消息,然后仅在收到对前一条消息的回复后才发送一条新消息。在这种情况下,当您send 时,您知道发送缓冲区始终为空。通过获取和/或设置 POSIX 标准 SO_SNDBUF 套接字选项,您可以知道您的套接字有足够的可用空间。在这种情况下,阻塞 send 的行为是否与非阻塞 send 不同?

我对 POSIX 规范的阅读原则上说“是”。我对 Linux 源代码的阅读在实践中说“是”。我当然可能是错的,但需要对 POSIX 或 Linux 更了解的人来演示它,到目前为止,他们都没有回答这个问题。

[最终(?)更新]

这是我认为 POSIX 允许/要求您假设的内容。

如果发送缓冲区中有足够的可用空间,则阻塞 send 不能永远阻塞。这与使用足够的空闲虚拟内存对calloc 的调用不能永远阻塞的意义相同。最终系统会找到发送数据所需的资源。

(请注意,当发送缓冲区已满时,情况并非如此,在这种情况下,阻塞 send 可能会永远阻塞,具体取决于套接字接收端发生的情况。)

但是,即使发送缓冲区中有足够的空间,非阻塞send 仍可能返回EWOULDBLOCK。因此,如果您使用非阻塞套接字,则无论您对发送缓冲区或其他任何内容了解多少,您的代码都必须处理这个问题。

【讨论】:

  • 您正在疯狂地推测发送缓冲区。它是在创建套接字时永久分配的,并且根据 TCP 的语义,它需要在 send() 调用之间持续存在。这就是它的用途。。 DV
  • @EJP - 好的,我停止推测并添加了对内核源代码的引用。请删除您的反对意见或证明此代码路径是不可能的。
  • Nemo,当然,您的更新 2 反映了我的用例,其中一次只有一条未确认的消息。消息大小在 1K - 3KB 之间。因此,分配了一个 4KB SO_SNDBUF。如果有足够的内存可用,还有其他区别吗?
  • @Jimm,我又更新了答案。请注意,正确的问题不是“Linux 今天发生了什么?”而是“如果我希望我的代码在今天和明天运行,我应该假设什么?”
【解决方案2】:

请记住,您不能始终确保您的发送将被快速执行。

例如,如果另一端的套接字没有使用 recv 读取,那么您的缓冲区将已满。

当然,如果您编写应用程序的两面并始终读取,我猜在性能方面不会有显着差异。

【讨论】:

  • 您忽略了部分问题。具体来说,以“如果应用程序可以确保...”开头的部分。前五个字。
  • 不是说“应用程序”也负责读取套接字是吗?
  • 第一句中假设应用程序可以确保发送缓冲区中有空间。你的问题与我无关。
【解决方案3】:

“确保套接字发送缓冲区中始终有空间”的唯一方法是在缓冲区已满时不发送。

您确实可以确定发送缓冲区中是否有空间 - 在某些系统上。如果在阻塞模式下没有空间,您可以 select() 用于可写性 - 在某些系统上。在其他系统上,您无法判断是否有空间和/或您无法在阻塞模式下 select()。

在这样的系统上,除了发送和阻塞,你没有任何好的实现选择,或者使用阻塞模式。

在能知道但不能select()的系统上,可以循环和休眠,但是不知道要休眠多长时间,所以会休眠太久,浪费时间,不像阻塞,会阻塞正确的时间长度。

【讨论】:

  • -1。引用 POSIX 规范(或 Linux 源代码),表明写入发送缓冲区中有空间的套接字对于阻塞/非阻塞套接字不能有不同的行为,我将删除我的反对票。
  • @Nemo 假设 引用我说过它不能引用的地方?
  • 问题是,“阻塞和非阻塞发送有什么区别吗?” (当已知发送缓冲区中有空间时)。我将您的回答解释为“不”,这是错误的。你打算说“是”吗?
  • @Nemo 你如何将这一切“解释”为一个简单的“不”对我来说是个谜。我正在解决他的问题的前提,以及它们在阻塞模式下无效的原因。关于非阻塞模式,我还没有说过一句话。这太荒谬了。
  • 他在问一个直截了当的问题。他把它命名得非常准确,并详细解释了它。我将您对他的问题的回答解释为“不”加上一堆评论。 (如果您只想提供评论,您应该在 cmets 中这样做。)您的评论是有效的,但您对他的问题的回答是错误的。如果您明确表示您对他的问题的回答是“是”,我将删除我的反对票。其实很简单。
猜你喜欢
  • 2023-03-28
  • 1970-01-01
  • 2012-01-15
  • 1970-01-01
  • 1970-01-01
  • 2018-11-15
  • 2016-07-06
  • 2014-06-24
  • 2012-10-31
相关资源
最近更新 更多