(unix domain socket)使用udp发送>=128K的消息会报ENOBUFS的错误

前端之家收集整理的这篇文章主要介绍了(unix domain socket)使用udp发送>=128K的消息会报ENOBUFS的错误前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

1、Unix domain socket简介

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位端口号构成。)。

2、问题描述

简单介绍了Unix域套接口之后,进入主题——描述我碰到的问题。由于unix域套接口用于本机间进程通信比网络套接口效率高,因为它是不经过协议栈的!在项目中选择了unix域的数据报套接口。在使用过程中碰到了如下,问题:发送<128K的消息时,客户、进程可以正常收发消息;发送>=128K的消息时,发送端(sendto)返回ENOBUFS的错误

服务器的代码如下:

服务器端
   
   
#include < stdio.h >
stdlib.h
sys / types.h socket.h un.h errno.h // define send and recv buf size #define BUFSIZE 512*1024 define unix domain socket path pmmanager "/tmp/pmmanager" pmapi "/tmp/pmapi" int main( argc, char ** argv)
{
rx_buf[BUFSIZE];
pmmanager_fd,ret;
socklen_t len;
struct
sockaddr_un pmmanager_addr,pmapi_addr;

create pmmanager socket fd pmmanager_fd = socket(AF_UNIX,SOCK_DGRAM, 0 );
if
(pmmanager_fd == - 1 )
{
perror(
" cannot create pmmanager fd. " }
unlink(pmmanager);
memset(
& pmmanager_addr,204)!important">,204)!important">sizeof (pmmanager_addr));
pmmanager_addr.sun_family
AF_UNIX;
strncpy(pmmanager_addr.sun_path,pmmanager,204)!important">(pmmanager_addr.sun_path)
bind pmmanager_fd to pmmanager_addr ret bind(pmmanager_fd,( sockaddr * ) (ret can not bind pmmanager_addr recvBufSize;
len
(recvBufSize);
getsockopt(pmmanager_fd,SOL_SOCKET,SO_RCVBUF,
recvBufSize,204)!important">len);
==-
getsocket error. printf( Before setsockopt,SO_RCVBUF-%d\n recvBufSize 512 * 1024 ;
setsockopt(pmmanager_fd,len);
setsockopt error.
Set recv buf successful,204)!important"> recvSize;
pmapi_addr,204)!important">(pmapi_addr));
(pmapi_addr);
==============wait for msg from pmapi====================\n
for (;;)
memset(rx_buf,204)!important">(rx_buf));
recvSize
recvfrom(pmmanager_fd,rx_buf,204)!important">(rx_buf),204)!important">(recvSize recvfrom error. Recved message from pmapi: %s\n }

客户端的代码如下:

客户端
BUFSIZE 250*1024 tx_buf[BUFSIZE];
pmapi_fd,204)!important"> pmapi_fd
(pmapi_fd cannot create pmapi fd. unlink(pmapi);
configure pmapi's addr
pmapi_addr.sun_family strncpy(pmapi_addr.sun_path,pmapi,204)!important">(pmapi_addr.sun_path) bind pmapi_fd to pmapi_addr bind(pmapi_fd,204)!important">bind error. sendBufSize;
(sendBufSize);
getsockopt(pmapi_fd,SO_SNDBUF,204)!important">sendBufSize,SO_SNDBUF-%d\n
sendBufSize setsockopt(pmapi_fd,204)!important">Set send buf successful,SO_SNDBUF-%d\n\n\n configure pmmanager's addr (pmmanager_addr) (pmmanager_addr);
sendSize
i;
(i
= ; i <= 4 ++ memset(tx_buf, ' 0 ' (tx_buf));
sprintf(tx_buf,204)!important">send msg %d to pmmanager.
%s,msg size - %d\n sendto(pmapi_fd,204)!important">(tx_buf),204)!important">(sendSize sendto error. Send message to pmmanager: %s\n\n\n

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

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

(注意:UDP套接口有发送缓冲区的大小,并且可以通过SO_SNDBUF套接口选项修改。不过它仅仅是写到套接口的UDP数据报的大小,因为UDP是不可靠的,它不必保存应用进程的数据拷贝,因此无需一个真正的发送缓冲区。)上面的代码已经设置了足够大的发送缓冲区大小。

4、我的尝试

在sendto发送>=128K大小的消息时,返回ENOBUFS错误

  • 我怀疑是否是sendto()的原因,我改用sendmsg(),未果还是返回这个错误
  • 有人说是:“发送消息太频繁,间隔太短”。其实项目中发送消息根本就不频繁,背着死马当活马医,未果还是返回这个错误
  • 尝试修改/proc/sys/net/core下面的各种相关选项,如
    未果,还是返回这个错误。(其它路径下的相关选项也试了,不行)
  • ?我无从下手了,不知道128K的这个限制在哪?既然“No buffer space available”,我怎样给他空间?

5、最终原因及解决办法(都是内核惹得祸!!)

至此,我实在没有办法了,不知道如何解决!但是从错误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 asystem 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 bemade to disk of a real memory segment thereby allowing the listener access tosome 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的限制!
代码
    
    
。。。。。。
size
- 131072 (DMA) 0 1 32 : tunables 8 4 : slabdata 65536 16 32768 16384 8192 2 4096 24 12 2048 14 7 1024 54 27 11 3 512 208 26 256 15 120 60 75 5 192 20 40 128 30 86 90 96 388 400 10 64 59 113 451 472 871 904 。。。。。。

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

/proc/slabinfo的信息如下,跟上面的有些差异:
kmalloc的最大限制是8192K,故我们运行上述程序没有问题!
原来都是内核惹得祸阿,害我困惑那么久!!!baidu和google都没有找到原因,因此分享此文,以警惕后者。
原文链接:https://www.f2er.com/bash/392944.html

猜你在找的Bash相关文章