阻塞和非阻塞发送之间是否有任何区别

时间:2012-12-04 17:49:31

标签: c linux tcp

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

3 个答案:

答案 0 :(得分:5)

阻塞和非阻塞send之间的唯一区别是内核是将进程置于休眠状态还是返回EWOULDBLOCK。所以在性能方面,应该没有区别。

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

现在,这是很多“maybes”和“mights”,更熟悉内核内部的人可能会告诉我我错了。但是,即使Linux今天没有这样的行为,它可能明天就会出现;你是否100%确定你永远不会在除Linux之外的任何东西上运行你的应用程序?

所以我不会用如此脆弱的假设来编写我的应用程序。我建议您决定阻塞或非阻塞语义是否对您自己的代码更有意义,并且不要尝试游戏内核的内部。

[更新]

我希望我不必深入研究Linux内部,但过于自信的downvoter让我想到了它。

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_memorytimeo值取决于这是否为非阻止发送。反过来,该函数会使进程进入休眠状态,或者根据EAGAIN返回timeo

[更新2]

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

我将此问题解释为“如果我知道我的套接字的发送缓冲区有足够的可用空间,那么阻塞和非阻塞send之间是否有任何区别 - 性能或其他方面插座?“

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

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

[最终(?)更新]

以下是我认为POSIX允许/要求您承担的事项。

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

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

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

答案 1 :(得分:-1)

请注意,您无法始终确保您的发送能够快速执行。

例如,如果未使用recv读取另一侧的套接字,则缓冲区将满。

当然,如果您编写应用程序的两面并且始终阅读,那么我认为性能不会有显着差异。

答案 2 :(得分:-1)

唯一可以“确保套接字发送缓冲区总是有空间”的方法是在它已满时不发送。

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

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

在你可以知道而不是select()的系统上,你可以循环和睡觉,但你不知道睡了多长时间,所以你会睡得太久而浪费时间,不像阻塞,这将阻止正确的时间长度。