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@H_301_25@ <@H_301_25@ stdio.h@H_301_25@ >@H_301_25@
stdlib.h@H_301_25@ sys@H_301_25@ /@H_301_25@ types.h@H_301_25@ socket.h@H_301_25@ un.h@H_301_25@ errno.h@H_301_25@ @H_301_25@ //@H_301_25@ define send and recv buf size@H_301_25@ @H_301_25@ #define@H_301_25@ BUFSIZE 512*1024@H_301_25@ define unix domain socket path@H_301_25@ pmmanager "/tmp/pmmanager"@H_301_25@ pmapi "/tmp/pmapi"@H_301_25@ int@H_301_25@ main(@H_301_25@ argc,@H_301_25@ char@H_301_25@ **@H_301_25@ argv)
{
@H_301_25@ rx_buf[BUFSIZE];
pmmanager_fd,ret;
socklen_t len;
struct@H_301_25@ sockaddr_un pmmanager_addr,pmapi_addr;
@H_301_25@ create pmmanager socket fd@H_301_25@ @H_301_25@ pmmanager_fd @H_301_25@ =@H_301_25@ socket(AF_UNIX,SOCK_DGRAM,@H_301_25@ 0@H_301_25@ );
if@H_301_25@ (pmmanager_fd @H_301_25@ ==@H_301_25@ @H_301_25@ -@H_301_25@ 1@H_301_25@ )
{
perror(@H_301_25@ "@H_301_25@ cannot create pmmanager fd.@H_301_25@ "@H_301_25@ }
unlink(pmmanager);
memset(@H_301_25@ &@H_301_25@ pmmanager_addr,204)!important">,204)!important">sizeof@H_301_25@ (pmmanager_addr));
pmmanager_addr.sun_family @H_301_25@ AF_UNIX;
strncpy(pmmanager_addr.sun_path,pmmanager,204)!important">(pmmanager_addr.sun_path)@H_301_25@ bind pmmanager_fd to pmmanager_addr@H_301_25@ ret @H_301_25@ bind(pmmanager_fd,(@H_301_25@ sockaddr@H_301_25@ *@H_301_25@ )@H_301_25@ (ret @H_301_25@ can not bind pmmanager_addr@H_301_25@ recvBufSize;
len @H_301_25@ (recvBufSize);
getsockopt(pmmanager_fd,SOL_SOCKET,SO_RCVBUF,@H_301_25@ recvBufSize,204)!important">len);
==-@H_301_25@ getsocket error.@H_301_25@ printf(@H_301_25@ Before setsockopt,SO_RCVBUF-%d\n@H_301_25@ recvBufSize @H_301_25@ @H_301_25@ 512@H_301_25@ *@H_301_25@ 1024@H_301_25@ ;
setsockopt(pmmanager_fd,len);
setsockopt error.@H_301_25@ Set recv buf successful,204)!important"> recvSize;
pmapi_addr,204)!important">(pmapi_addr));
(pmapi_addr);
==============wait for msg from pmapi====================\n@H_301_25@ for@H_301_25@ (;;)
memset(rx_buf,204)!important">(rx_buf));
recvSize @H_301_25@ recvfrom(pmmanager_fd,rx_buf,204)!important">(rx_buf),204)!important">(recvSize @H_301_25@ recvfrom error.@H_301_25@ Recved message from pmapi: %s\n@H_301_25@ }@H_301_25@
客户端的代码如下:
3、可能碰到的另外一个问题@H_301_25@
如果你没有设置足够大的发送缓冲区大小,你很有可能碰到EMSGSIZE的错误!因为应用程序写了一个大于套机口发送缓冲区大小的数据报,内核报EMSGSIZE错误。如下图:
(注意:UDP套接口有发送缓冲区的大小,并且可以通过SO_SNDBUF套接口选项修改。不过它仅仅是写到套接口的UDP数据报的大小,因为UDP是不可靠的,它不必保存应用进程的数据拷贝,因此无需一个真正的发送缓冲区。)上面的代码已经设置了足够大的发送缓冲区大小。
4、我的尝试
在sendto发送>=128K大小的消息时,返回ENOBUFS错误。
- 我怀疑是否是sendto()的原因,我改用sendmsg(),未果还是返回这个错误。
- 有人说是:“发送消息太频繁,间隔太短”。其实项目中发送消息根本就不频繁,背着死马当活马医,未果还是返回这个错误。
- 尝试修改/proc/sys/net/core下面的各种相关选项,如
未果,还是返回这个错误。(其它路径下的相关选项也试了,不行) - ?我无从下手了,不知道128K的这个限制在哪?既然“No buffer space available”,我怎样给他空间?
5、最终原因及解决办法(都是内核惹得祸!!)
- 在2.6.21内核中(这就是我们公司服务器的内核版本),slab分配器最大支持的size为128K(详情可见/proc/slabinfo)。
- 在2.6.31内核中,slab分配器最大支持的size大小为32M。
。。。。。。
size@H_301_25@ -@H_301_25@ 131072@H_301_25@ (DMA) @H_301_25@ 0@H_301_25@ @H_301_25@ 1@H_301_25@ 32@H_301_25@ : tunables @H_301_25@ 8@H_301_25@ 4@H_301_25@ : slabdata @H_301_25@ 65536@H_301_25@ 16@H_301_25@ 32768@H_301_25@ 16384@H_301_25@ 8192@H_301_25@ 2@H_301_25@ 4096@H_301_25@ 24@H_301_25@ 12@H_301_25@ 2048@H_301_25@ 14@H_301_25@ 7@H_301_25@ 1024@H_301_25@ 54@H_301_25@ 27@H_301_25@ 11@H_301_25@ 3@H_301_25@ 512@H_301_25@ 208@H_301_25@ 26@H_301_25@ 256@H_301_25@ 15@H_301_25@ 120@H_301_25@ 60@H_301_25@ 75@H_301_25@ 5@H_301_25@ 192@H_301_25@ 20@H_301_25@ 40@H_301_25@ 128@H_301_25@ 30@H_301_25@ 86@H_301_25@ 90@H_301_25@ 96@H_301_25@ 388@H_301_25@ 400@H_301_25@ 10@H_301_25@ 64@H_301_25@ 59@H_301_25@ 113@H_301_25@ 451@H_301_25@ 472@H_301_25@ 871@H_301_25@ 904@H_301_25@ 。。。。。。@H_301_25@
我在Ubuntu 10.10上测试,不会报ENOBUFS的错误。内核版本为: