信号在阻塞模式下打断了发送方法

4

我正在使用阻塞模式下的socket进行编程,我对send方法有疑问。

send方法的man页面中,它说:

[EINTR] 在传输任何数据之前,信号中断了系统调用。

这意味着如果在传输任何数据之前,信号中断了系统调用,则send将返回-1,并且errno将设置为EINTR

我的问题是,如果在信号中断系统调用时已经发送了一部分数据,会返回什么。似乎不应该返回-1,因为它已经发送了一些数据。我认为它将返回已传输的数据量,这意味着在阻塞模式下,send方法可能会返回少于您作为第三个参数传递的数据量。

ssize_t send(int socket, const void *buffer, size_t length, int flags);


这个问题与我的问题类似,但更有用。 - KudoCC
3个回答

2

这份文档讲解得十分清晰。

返回值

如果成功,这些调用将返回发送的字节数。如果出现错误,则返回-1,并适当设置errno。

[...]

EINTR 在传输任何数据之前发生了信号;

send()函数要么返回:

  • 发送的字节数
  • 要么返回-1

如果返回-1,则原因通过errno的值表示。

如果errno等于EINTR,则表示在send()接收到任何数据之前被信号中断。

由此可知,如果数据已经被接收,无论是否收到信号,send()函数都不会返回-1


我和你有相同的想法,但我不确定。这是因为我不明白为什么“在传输任何数据之前发生信号”会导致错误,如果“发生信号”导致错误,那么“在传输一些数据之后发生信号”为什么是成功的。 - KudoCC
@KudoCC:如果你遇到了不同的行为,那么要么是你的程序有问题,要么是send()的实现有问题。 - alk
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - alk
谢谢,我想要的是你确认一下 :) - KudoCC
@KudoCC:这句话 "从上面的信息可以得出一个安全的结论……" 不是足够确定吗? - alk
显示剩余2条评论

2
其他回答已经很清楚了,但是在阅读了您的一些评论后,我想补充一些进一步的信息。
首先,您对EINTR的理解是错误的。在系统调用中被信号打断并不应该被视为错误。在慢速系统调用中(慢速系统调用是那些可能永远阻塞的调用,例如open(2)在某些文件类型上的调用,比如终端设备,accept(2),read(2)和write(2)在某些设备上的调用,包括套接字等),EINTR的原因是:如果您的程序在系统调用中被阻塞,并且在仍然阻塞时捕获到了一个信号,那么很可能(但不是必须的)信号处理程序在您的程序中改变了状态,因此调用会提前返回EINTR,以便让您有机会做任何您想做的事情。这不是像EINVAL或EBADF或其他“真正”的错误 - 这只是内核告诉您一个信号被捕获的方式。
如果您不想做任何事情,那么可以在设置信号处理程序时将SA_RESTART标志设置在struct sigaction的sa_flags字段上(这会导致系统调用自动重新启动),或者在返回-1并且errno设置为EINTR时显式调用send(2)。
底线是,内核并没有本质上的限制强制它在捕获信号时返回用户空间。相反,EINTR只是一个方便的行为,开发人员可能会发现有用。
如果内核正在传输数据并且引发了一个信号,那么这没什么大不了的:如果数据正在传输,则系统调用正在进行中。内核正在进程上下文中执行代表调用它的程序的系统调用,因此从技术上讲,该进程不再处于睡眠状态。如果信号到达,它将保持挂起状态,直到内核决定是时候将其传递给进程 - 最有可能的情况是,在send(2)返回时发生这种情况。

感谢您承担了提供大量背景信息的负担! :-) 1+ 随便说一句 - alk
谢谢你的回答,我已经注意到它一段时间并尝试理解,但是我发现除非有关于内核/信号等方面的背景知识,否则很难理解。 - KudoCC
@KudoCC 很高兴能帮到你!也谢谢你提出这个很棒的问题 :) - Filipe Gonçalves

-1
[EINTR] 表示在传输任何数据之前,信号中断了系统调用。
这意味着如果 send() 开始传输数据,它不会被任何信号中断。因此,传输将阻塞接收信号直到完成。send() 可能返回比您作为第三个参数传递的字节数少的情况通常是由于网络问题,例如数据包丢失。

1
你有一些参考资料来支持你的回答吗?我相信TCP会处理丢失的数据包,而不会使“send”方法返回。 - KudoCC
你可以查看设备驱动程序如何实现它们的 write() 函数。send() 做了很多与 write() 相似的事情,但是涉及到网络相关部分。在 write() 的实现中,你会发现像这样的代码:if (signal_pending(current)) return -ERESTARTSYS; 如果有一些信号挂起,这将中断传输。http://www.makelinux.net/ldd3/chp-6-sect-2 可能会对你有所帮助。 - Douglas Su
套接字是一种特殊的I/O,read()send()几乎没有区别。您可以查看https://dev59.com/GnI-5IYBdhLWcg3wkJMA来了解更多信息。此外,如果您真正了解Linux操作系统的底层,则所有这些操作都基于虚拟文件系统层进行,无论是网络套接字还是在磁盘上的文件。 - Douglas Su
当然!write()send()是基于虚拟文件系统的高级函数,不仅适用于用户空间,还适用于内核空间的某些部分。 - Douglas Su
1
你认为 "...关于返回的字节数较少的声明..." 有什么问题? - alk
显示剩余10条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接