unix domain socket.docx

上传人:b****4 文档编号:24279066 上传时间:2023-05-26 格式:DOCX 页数:11 大小:262.85KB
下载 相关 举报
unix domain socket.docx_第1页
第1页 / 共11页
unix domain socket.docx_第2页
第2页 / 共11页
unix domain socket.docx_第3页
第3页 / 共11页
unix domain socket.docx_第4页
第4页 / 共11页
unix domain socket.docx_第5页
第5页 / 共11页
点击查看更多>>
下载资源
资源描述

unix domain socket.docx

《unix domain socket.docx》由会员分享,可在线阅读,更多相关《unix domain socket.docx(11页珍藏版)》请在冰豆网上搜索。

unix domain socket.docx

unixdomainsocket

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

2010-12-0415:

49by吴秦,11211阅读,3评论,收藏,编辑

一个困扰我两天的问题,

Google和Baidu没有找到解决方法!

此文为记录这个问题,并给出原因和解决方法。

1、Unixdomainsocket简介

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

#include

#include

#include

#include

#include

//definesendandrecvbufsize

#defineBUFSIZE512*1024

//defineunixdomainsocketpath

#definepmmanager"/tmp/pmmanager"

#definepmapi"/tmp/pmapi"

intmain(intargc,char**argv)

{

charrx_buf[BUFSIZE];

intpmmanager_fd,ret;

socklen_tlen;

structsockaddr_unpmmanager_addr,pmapi_addr;

//createpmmanagersocketfd

pmmanager_fd=socket(AF_UNIX,SOCK_DGRAM,0);

if(pmmanager_fd==-1)

{

perror("cannotcreatepmmanagerfd.");

}

unlink(pmmanager);

memset(&pmmanager_addr,0,sizeof(pmmanager_addr));

pmmanager_addr.sun_family=AF_UNIX;

strncpy(pmmanager_addr.sun_path,pmmanager,sizeof(pmmanager_addr.sun_path)-1);

//bindpmmanager_fdtopmmanager_addr

ret=bind(pmmanager_fd,(structsockaddr*)&pmmanager_addr,sizeof(pmmanager_addr));

if(ret==-1)

{

perror("cannotbindpmmanager_addr");

}

intrecvBufSize;

len=sizeof(recvBufSize);

ret=getsockopt(pmmanager_fd,SOL_SOCKET,SO_RCVBUF,&recvBufSize,&len);

if(ret==-1)

{

perror("getsocketerror.");

}

printf("Beforesetsockopt,SO_RCVBUF-%d\n",recvBufSize);

recvBufSize=512*1024;

ret=setsockopt(pmmanager_fd,SOL_SOCKET,SO_RCVBUF,&recvBufSize,len);

if(ret==-1)

{

perror("setsockopterror.");

}

ret=getsockopt(pmmanager_fd,SOL_SOCKET,SO_RCVBUF,&recvBufSize,&len);

if(ret==-1)

{

perror("getsocketerror.");

}

printf("Setrecvbufsuccessful,SO_RCVBUF-%d\n",recvBufSize);

intrecvSize;

memset(&pmapi_addr,0,sizeof(pmapi_addr));

len=sizeof(pmapi_addr);

printf("==============waitformsgfrompmapi====================\n");

for(;;)

{

memset(rx_buf,0,sizeof(rx_buf));

recvSize=recvfrom(pmmanager_fd,rx_buf,sizeof(rx_buf),0,(structsockaddr*)&pmapi_addr,&len);

if(recvSize==-1)

{

perror("recvfromerror.");

}

printf("Recvedmessagefrompmapi:

%s\n",rx_buf);

}

}

客户端的代码如下:

客户端

#include

#include

#include

#include

#include

#include

//definesendandrecvbufsize

#defineBUFSIZE250*1024

//defineunixdomainsocketpath

#definepmmanager"/tmp/pmmanager"

#definepmapi"/tmp/pmapi"

intmain(intargc,char**argv)

{

chartx_buf[BUFSIZE];

intpmapi_fd,ret;

socklen_tlen;

structsockaddr_unpmmanager_addr,pmapi_addr;

//createpmmanagersocketfd

pmapi_fd=socket(AF_UNIX,SOCK_DGRAM,0);

if(pmapi_fd==-1)

{

perror("cannotcreatepmapifd.");

}

unlink(pmapi);

//configurepmapi'saddr

memset(&pmapi_addr,0,sizeof(pmapi_addr));

pmapi_addr.sun_family=AF_UNIX;

strncpy(pmapi_addr.sun_path,pmapi,sizeof(pmapi_addr.sun_path)-1);

//bindpmapi_fdtopmapi_addr

ret=bind(pmapi_fd,(structsockaddr*)&pmapi_addr,sizeof(pmapi_addr));

if(ret==-1)

{

perror("binderror.");

}

intsendBufSize;

len=sizeof(sendBufSize);

ret=getsockopt(pmapi_fd,SOL_SOCKET,SO_SNDBUF,&sendBufSize,&len);

if(ret==-1)

{

perror("getsocketerror.");

}

printf("Beforesetsockopt,SO_SNDBUF-%d\n",sendBufSize);

sendBufSize=512*1024;

ret=setsockopt(pmapi_fd,SOL_SOCKET,SO_SNDBUF,&sendBufSize,len);

if(ret==-1)

{

perror("setsockopterror.");

}

ret=getsockopt(pmapi_fd,SOL_SOCKET,SO_SNDBUF,&sendBufSize,&len);

if(ret==-1)

{

perror("getsocketerror.");

}

printf("Setsendbufsuccessful,SO_SNDBUF-%d\n\n\n",sendBufSize);

//configurepmmanager'saddr

memset(&pmmanager_addr,0,sizeof(pmmanager_addr));

pmmanager_addr.sun_family=AF_UNIX;

strncpy(pmmanager_addr.sun_path,pmmanager,sizeof(pmmanager_addr)-1);

len=sizeof(pmmanager_addr);

intsendSize=0;

inti;

for(i=1;i<=4;i++)

{

memset(tx_buf,'0',sizeof(tx_buf));

sprintf(tx_buf,"sendmsg%dtopmmanager.",i);

printf("%s,msgsize-%d\n",tx_buf,sizeof(tx_buf));

sendSize=sendto(pmapi_fd,tx_buf,sizeof(tx_buf),0,(structsockaddr*)&pmmanager_addr,len);

if(sendSize==-1)

{

perror("sendtoerror.");

}

printf("Sendmessagetopmmanager:

%s\n\n\n",tx_buf);

}

}

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

如果你没有设置足够大的发送缓冲区大小,你很有可能碰到EMSGSIZE的错误!

因为应用程序写了一个大于套机口发送缓冲区大小的数据报,内核报EMSGSIZE错误。

如下图:

(注意:

UDP套接口有发送缓冲区的大小,并且可以通过SO_SNDBUF套接口选项修改。

不过它仅仅是写到套接口的UDP数据报的大小,因为UDP是不可靠的,它不必保存应用进程的数据拷贝,因此无需一个真正的发送缓冲区。

)上面的代码已经设置了足够大的发送缓冲区大小。

4、我的尝试

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

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

∙有人说是:

“发送消息太频繁,间隔太短”。

其实项目中发送消息根本就不频繁,背着死马当活马医,未果还是返回这个错误。

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

 

未果,还是返回这个错误。

(其它路径下的相关选项也试了,不行)

∙?

我无从下手了,不知道128K的这个限制在哪?

既然“Nobufferspaceavailable”,我怎样给他空间?

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

至此,我实在没有办法了,不知道如何解决!

但是从错误ENOBUFS的说明:

ENOBUFSmeansthereisnosufficientmemoryavailableandthesystem(kernel) cannotallocateanymore. Applicationwillusuallyretrytheoperationwhenitdetectsthiserrorfroma systemcallsinceitindicatesthereisatransientresourceshortage. ItistheOperatingsystemthatrefusestheresourcerequestfromthelistener. ThevirtualmemoryallocationroutineoftheOSwilldetermineifaswapcanbe madetodiskofarealmemorysegmenttherebyallowingthelisteneraccessto somemorerealmemory.

可以看出一些端倪,这肯定跟内存分配有关!

而且限制在分配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()会失败,并返回nobuffer的错误。

建议:

对于本地进程通信,可以使用其它的IPC方式,进行数据通信,如shm、pipe等。

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

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

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

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

附/proc/slabinfo信息:

size-131072即128K的限制!

代码

size-131072(DMA)00131072132:

tunables840:

slabdata000

size-13107200131072132:

tunables840:

slabdata000

size-65536(DMA)0065536116:

tunables840:

slabdata000

size-655360065536116:

tunables840:

slabdata000

size-32768(DMA)003276818:

tunables840:

slabdata000

size-32768003276818:

tunables840:

slabdata000

size-16384(DMA)001638414:

tunables840:

slabdata000

size-16384001638414:

tunables840:

slabdata000

size-8192(DMA)00819212:

tunables840:

slabdata000

size-819200819212:

tunables840:

slabdata000

size-4096(DMA)00409611:

tunables24120:

slabdata000

size-409644409611:

tunables24120:

slabdata440

size-2048(DMA)00204821:

tunables24120:

slabdata000

size-20481214204821:

tunables24120:

slabdata770

size-1024(DMA)00102441:

tunables54270:

slabdata000

size-10241112102441:

tunables54270:

slabdata330

size-512(DMA)0051281:

tunables54270:

slabdata000

size-51220820851281:

tunables54270:

slabdata26260

size-256(DMA)00256151:

tunables120600:

slabdata000

size-2567575256151:

tunables120600:

slabdata550

size-192(DMA)00192201:

tunables120600:

slabdata000

size-1924040192201:

tunables120600:

slabdata220

size-128(DMA)00128301:

tunables120600:

slabdata000

size-1288690128301:

tunables120600:

slabdata330

size-96(DMA)0096401:

tunables120600:

slabdata000

size-9638840096401:

tunables120600:

slabdata10100

size-64(DMA)0064591:

tunables120600:

slabdata000

size-32(DMA)00321131:

tunables120600:

slabdata000

size-6445147264591:

tunables120600:

slabdata880

size-32871904321131:

tunables120600:

slabdata880

我在Ubuntu10.10上测试,不会报ENOBUFS的错误。

内核版本为:

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

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

原来都是内核惹得祸阿,害我困惑那么久!

baidu和google都没有找到原因,因此分享此文,以警惕后者。

作者:

吴秦

出处:

本文基于署名2.5中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名吴秦(包含链接).

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 初中教育 > 理化生

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1