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

时间:2022-10-23 20:27:33

If the application can ensure that there would always be space in the socket's send buffer, would blocking and non blocking send have same performance? In this scenario, is there any advantages in either approach over the other?

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

3 个解决方案

#1


5  

The only difference between blocking and non-blocking send is whether the kernel puts your process to sleep or returns EWOULDBLOCK. So in terms of performance, there should be no difference.

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

However, I doubt your implicit assumption that the send cannot block just because the send buffer has free space. Imagine an idle socket on a system that places heavy demands on memory. I would not necessarily expect the kernel to "pin" the physical pages for your send buffer; I would expect it to use that memory for something useful instead. And then when you try to send, the kernel will need to grab a free page for the send buffer; and if there are no such pages available, it might decide to return EWOULDBLOCK instead of waiting on (say) swap.

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

Now, that is a lot of "maybes" and "mights", and someone more familiar with the kernel's internals could tell me I am wrong. But even if Linux does not behave this way today, it might tomorrow; and are you 100% sure you will never run your application on anything other than Linux, ever?

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

So I would not write my application with such a fragile assumption. I suggest you decide whether blocking or non-blocking semantics make more sense for your own code, and do not try to game the kernel's internals.

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

[Update]

[更新]

I was hoping I would not have to dig in to Linux internals, but an overconfident downvoter has driven me to it.

我希望我不必深入研究Linux内部,但是一个过于自信的downvoter驱使我去了它。

Start with net/ipv4/tcp.c at the "new_segment" label:

从“new_segment”标签处的net / ipv4 / tcp.c开始:

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;

See how "wait_for_sndbuf" is distinct from "wait_for_memory"? That is what I am talking about.

看看“wait_for_sndbuf”与“wait_for_memory”有什么不同?这就是我所说的。

At the "wait_for_memory" label, there is a call to sk_stream_wait_memory with a timeo value that depends on whether this is a non-blocking send. That function, in turn, either puts the process to sleep or returns EAGAIN depending on timeo.

在“wait_for_memory”标签处,调用sk_stream_wait_memory,其timeo值取决于这是否为非阻塞发送。反过来,该函数要么将进程置于睡眠状态,要么根据timeo返回EAGAIN。

[Update 2]

[更新2]

Just to be clear what question I am answering...

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

I interpret this question to be, "If I know that my socket's send buffer has sufficient free space, is there any difference -- performance or otherwise -- between a blocking and a non-blocking send on that socket?"

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

The premise is certainly possible if, for example, your protocol is to send one message, and then only send a new message after receiving a reply to a previous message. In this case, you know the send buffer is always empty when you send. By getting and/or setting the POSIX-standard SO_SNDBUF socket option, you can know that your socket has adequate free space. In this case, can a blocking send behave differently from a non-blocking send?

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

My reading of the POSIX spec says "yes" in principle. My reading of the Linux source code says "yes" in practice. I could certainly be wrong, but it would take someone more knowledgeable about POSIX or Linux to demonstrate it, and none of them have answered this question so far.

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

[Final (?) update]

[最终(?)更新]

Here is what I believe POSIX allows/requires you to assume.

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

If there is adequate free space in the send buffer, then a blocking send cannot block forever. This is the same sense in which a call to calloc with adequate free virtual memory cannot block forever. Eventually the system will find the resources it needs to send your data.

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

(Note the same is not true when the send buffer is full, in which case a blocking send might block forever depending on what is happening at the receiving end of the socket.)

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

However, even when there is adequate space in the send buffer, a non-blocking send might still return EWOULDBLOCK. So if you use non-blocking sockets, your code must handle this regardless of what you know about the send buffer or anything else.

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

#2


-1  

Keep in mind that you cannot always be sure your send will be executed rapidly.

请记住,您无法始终确保您的发送将快速执行。

For instance, if the socket on the other side is not read using recv, your buffer will be full.

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

Of course, if you write both sides of the application and always read, there would not be a significant difference concerning performance i guess.

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

#3


-1  

The only way you can 'ensure that there would always be room in the socket send buffer' is by not sending when it is full.

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

You can indeed determine whether there is room in the send buffer - on some systems. If there isn't room in blocking mode, you can select() for writability - on some systems. On other systems you can't tell whether there is room and/or you can't select() in blocking mode.

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

On such systems you don't have any good implementation choices except to send and block, or else use blocking mode.

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

On the systems where you can know but not select(), you can loop and sleep, but you can't know how long to sleep for, so you will sleep for too long and waste time, unlike blocking, which will block for exactly the right length of time.

在你可以知道而不是select()的系统上,你可以循环和睡眠,但你不知道要睡多长时间,所以你会睡得太久而浪费时间,不像阻塞,这会阻塞适当的时间长度。

#1


5  

The only difference between blocking and non-blocking send is whether the kernel puts your process to sleep or returns EWOULDBLOCK. So in terms of performance, there should be no difference.

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

However, I doubt your implicit assumption that the send cannot block just because the send buffer has free space. Imagine an idle socket on a system that places heavy demands on memory. I would not necessarily expect the kernel to "pin" the physical pages for your send buffer; I would expect it to use that memory for something useful instead. And then when you try to send, the kernel will need to grab a free page for the send buffer; and if there are no such pages available, it might decide to return EWOULDBLOCK instead of waiting on (say) swap.

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

Now, that is a lot of "maybes" and "mights", and someone more familiar with the kernel's internals could tell me I am wrong. But even if Linux does not behave this way today, it might tomorrow; and are you 100% sure you will never run your application on anything other than Linux, ever?

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

So I would not write my application with such a fragile assumption. I suggest you decide whether blocking or non-blocking semantics make more sense for your own code, and do not try to game the kernel's internals.

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

[Update]

[更新]

I was hoping I would not have to dig in to Linux internals, but an overconfident downvoter has driven me to it.

我希望我不必深入研究Linux内部,但是一个过于自信的downvoter驱使我去了它。

Start with net/ipv4/tcp.c at the "new_segment" label:

从“new_segment”标签处的net / ipv4 / tcp.c开始:

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;

See how "wait_for_sndbuf" is distinct from "wait_for_memory"? That is what I am talking about.

看看“wait_for_sndbuf”与“wait_for_memory”有什么不同?这就是我所说的。

At the "wait_for_memory" label, there is a call to sk_stream_wait_memory with a timeo value that depends on whether this is a non-blocking send. That function, in turn, either puts the process to sleep or returns EAGAIN depending on timeo.

在“wait_for_memory”标签处,调用sk_stream_wait_memory,其timeo值取决于这是否为非阻塞发送。反过来,该函数要么将进程置于睡眠状态,要么根据timeo返回EAGAIN。

[Update 2]

[更新2]

Just to be clear what question I am answering...

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

I interpret this question to be, "If I know that my socket's send buffer has sufficient free space, is there any difference -- performance or otherwise -- between a blocking and a non-blocking send on that socket?"

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

The premise is certainly possible if, for example, your protocol is to send one message, and then only send a new message after receiving a reply to a previous message. In this case, you know the send buffer is always empty when you send. By getting and/or setting the POSIX-standard SO_SNDBUF socket option, you can know that your socket has adequate free space. In this case, can a blocking send behave differently from a non-blocking send?

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

My reading of the POSIX spec says "yes" in principle. My reading of the Linux source code says "yes" in practice. I could certainly be wrong, but it would take someone more knowledgeable about POSIX or Linux to demonstrate it, and none of them have answered this question so far.

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

[Final (?) update]

[最终(?)更新]

Here is what I believe POSIX allows/requires you to assume.

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

If there is adequate free space in the send buffer, then a blocking send cannot block forever. This is the same sense in which a call to calloc with adequate free virtual memory cannot block forever. Eventually the system will find the resources it needs to send your data.

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

(Note the same is not true when the send buffer is full, in which case a blocking send might block forever depending on what is happening at the receiving end of the socket.)

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

However, even when there is adequate space in the send buffer, a non-blocking send might still return EWOULDBLOCK. So if you use non-blocking sockets, your code must handle this regardless of what you know about the send buffer or anything else.

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

#2


-1  

Keep in mind that you cannot always be sure your send will be executed rapidly.

请记住,您无法始终确保您的发送将快速执行。

For instance, if the socket on the other side is not read using recv, your buffer will be full.

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

Of course, if you write both sides of the application and always read, there would not be a significant difference concerning performance i guess.

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

#3


-1  

The only way you can 'ensure that there would always be room in the socket send buffer' is by not sending when it is full.

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

You can indeed determine whether there is room in the send buffer - on some systems. If there isn't room in blocking mode, you can select() for writability - on some systems. On other systems you can't tell whether there is room and/or you can't select() in blocking mode.

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

On such systems you don't have any good implementation choices except to send and block, or else use blocking mode.

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

On the systems where you can know but not select(), you can loop and sleep, but you can't know how long to sleep for, so you will sleep for too long and waste time, unlike blocking, which will block for exactly the right length of time.

在你可以知道而不是select()的系统上,你可以循环和睡眠,但你不知道要睡多长时间,所以你会睡得太久而浪费时间,不像阻塞,这会阻塞适当的时间长度。