天天看点

(unix domain socket)使用udp发送>=128K的消息会报ENOBUFS的错误1、Unix domain socket简介2、问题描述4、我的尝试5、最终原因及解决办法(都是内核惹得祸!!)

unix域协议并不是一个实际的协议族,而是在单个主机上执行客户/服务器通信的一种方法,所用api于在不同主机上执行客户/服务器通信所有的

api(套接字api,如af_inet、af_inet6等类型的api)相同。unix域协议可以视为是进程之间本地通信ipc的一种。

unix域提供两类套接口:字节流套接口(类似tcp)和数据报套接口(类似udp)。使用unix域套接口的理由有三:

unix域套接口往往比位于同一主机的tcp套接口快出一倍。

unix域套接口可用于在同一主机上的不同进程之间传递描述字。

unix域套接口把客户的凭证(用户id和用户组id)提供给服务器,从而实现能够提供额外的安全检查措施。

unix域中用域标识客户和服务器的协议地址是普通文件系统中的路径名(类比:ipv4协议的地址由一个32位地址和一个16位端口号构成,ipv6协议的地址由一个128位地址和16位端口号构成。)。

简单介绍了unix域套接口之后,进入主题——描述我碰到的问题。由于unix域套接口用于本机间进程通信比网络套接口效率高,因为它是不经过协议

栈的!在项目中选择了unix域的数据报套接口。在使用过程中碰到了如下,问题:发送<128k的消息时,客户、进程可以正常收发消息;发

送>=128k的消息时,发送端(sendto)返回enobufs的错误。

服务器的代码如下:

客户端的代码如下:

3、可能碰到的另外一个问题

如果你没有设置足够大的发送缓冲区大小,你很有可能碰到emsgsize的错误!因为应用程序写了一个大于套机口发送缓冲区大小的数据报,内核报emsgsize错误。如下图:

(unix domain socket)使用udp发送>=128K的消息会报ENOBUFS的错误1、Unix domain socket简介2、问题描述4、我的尝试5、最终原因及解决办法(都是内核惹得祸!!)

(注意:udp套接口有发送缓冲区的大小,并且可以通过so_sndbuf套接口选项修改。不过它仅仅是写到套接口的udp数据报的大小,因为

udp是不可靠的,它不必保存应用进程的数据拷贝,因此无需一个真正的发送缓冲区。)上面的代码已经设置了足够大的发送缓冲区大小。

在sendto发送>=128k大小的消息时,返回enobufs错误。

我怀疑是否是sendto()的原因,我改用sendmsg(),未果还是返回这个错误。

有人说是:“发送消息太频繁,间隔太短”。其实项目中发送消息根本就不频繁,背着死马当活马医,未果还是返回这个错误。

尝试修改/proc/sys/net/core下面的各种相关选项,如

(unix domain socket)使用udp发送>=128K的消息会报ENOBUFS的错误1、Unix domain socket简介2、问题描述4、我的尝试5、最终原因及解决办法(都是内核惹得祸!!)

未果,还是返回这个错误。(其它路径下的相关选项也试了,不行)

?我无从下手了,不知道128k的这个限制在哪?既然“no buffer space available”,我怎样给他空间?

至此,我实在没有办法了,不知道如何解决!但是从错误enobufs的说明:

enobufs means there is no sufficient memory available and the

system(kernel)  can not allocate any more. application will usually

retry the operation when it detects this error from a system call since

it indicates there is a transient resource shortage. it is the operating

system that refuses the resource request from the listener. the virtual

memory allocation routine of the os will determine if a swap can

be made to disk of a real memory segment thereby allowing the listener

access to some more real memory.

可以看出一些端倪,这肯定跟内存分配有关!而且限制在分配128k就失败!利用socket进行进程间的通信,需要经过linux内核:进程1

将数据写到内核,进程2从内核读取数据。内核必须申请一个空间来存放数据包!实际上,socket发送数据包时,需要从slab中申请一块cache存放

数据包。

在2.6.21内核中(这就是我们公司服务器的内核版本),slab分配器最大支持的size为128k(详情可见/proc/slabinfo)。

在2.6.31内核中,slab分配器最大支持的size大小为32m。

所以2.6.21内核上,发送大于128k的数据包时,kmalloc()会失败,并返回no buffer的错误。建议:对于本地进程通信,可以使用其它的ipc方式,进行数据通信,如shm、pipe等。

找出了原因,可以采用以下方式来解决该问题:

升级内核,或修改内核的这个限制。

改用unix 域udp套接口为unix域tcp套接口(最终我们采用的方式)。

改用其它的ipc方式(这个涉及到太多的修改,故我们放弃使用)。

附/proc/slabinfo 信息:size-131072即128k的限制!

我在ubuntu 10.10上测试,不会报enobufs的错误。内核版本为:

(unix domain socket)使用udp发送>=128K的消息会报ENOBUFS的错误1、Unix domain socket简介2、问题描述4、我的尝试5、最终原因及解决办法(都是内核惹得祸!!)

/proc/slabinfo的信息如下,跟上面的有些差异:

(unix domain socket)使用udp发送>=128K的消息会报ENOBUFS的错误1、Unix domain socket简介2、问题描述4、我的尝试5、最终原因及解决办法(都是内核惹得祸!!)

kmalloc的最大限制是8192k,故我们运行上述程序没有问题!

原来都是内核惹得祸阿,害我困惑那么久!!!baidu和google都没有找到原因,因此分享此文,以警惕后者。

继续阅读