阻塞和非阻塞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。因此,如果您使用非阻塞套接字,则无论您对发送缓冲区或其他任何内容了解多少,您的代码都必须处理这个问题。