在这篇文章中,我希望讨论一个常见的编码错误,它可能会导致一个模块在创建TCP套接字时耗尽或至少严重消耗可用的stcp设备克隆数量。
每次调用套接字函数都会导致STCP创建一个新的stcp设备的克隆。可以创建的最大克隆数量由devices.tin条目中的clone_limit字段控制。
/ =name stcp.m17
=module_name m17
=device_type streams
=access_list_name stcp_access
=streams_driver stcp
=clone_limit 5120
=comment 提供TCP API
|
通过在analyze_system中转储设备结构并查看clone_count值,可以看到当前有多少个克隆在使用。如果clone_count等于clone_limit,那么对socket函数的调用将返回一个e$clone_limit_exceeded。"The clone limit for the device has been exceeded"错误。
作为:match clone; dump_dvt -name stcp.m17。
clone_limit: 5120
clone_count: 42
cloned_from: -27271
remote_clone_limit: 0
|
一般情况下,创建克隆设备的套接字调用后面都会有一个 连接打电话或 绑听的调用。这时你执行"netstat命令"就可以看到相应的条目。
netstat -numeric -all_sockets. 活动连接(包括服务器) Proto Recv-Q Send-Q 本地地址 国外地址(州) tcp 0 0 172.16.124.217:23 192.168.109.22:50038 ESTABLISHED(建立) tcp 0 0 172.16.124.217:22 172.16.124.24:54987 ESTABLISHED(简体中文) tcp 0 0 10.20.1.1:37 10.20.1.26:1528 TIME_WAIT tcp 0 0 10.20.1.1:37 10.20.1.27:1579 TIME_WAIT tcp 0 0 172.16.124.217:61780 192.168.109.22:23 ESTABLISHED(建立) tcp 0 0 172.16.124.217:22 172.16.124.50:17421 ESTABLISHED(建立) tcp 0 0 172.16.124.217:22 172.16.124.50:17658 ESTABLISHED(建立) tcp 0 0 *:23 *:* LISTEN(听) tcp 0 0 *:6666 *:* LISTEN tcp 0 0 *:21 *:* LISTEN(听) tcp 0 0 *:3000 *:* LISTEN tcp 0 0 *:7 *:* LISTEN(听)。 tcp 0 0 *:9 *:* LISTEN(听) tcp 0 0 *:13 *:* LISTEN(听) tcp 0 0 *:19 *:* LISTEN(听) tcp 0 0 *:37 *:* LISTEN(听) tcp 0 0 *:901 *:* LISTEN(听) tcp 0 0 *:1414 *:* LISTEN tcp 0 0 *:81 *:* 听筒 tcp 0 0 10.20.1.1:37 10.20.1.9:3633 时间_等待 tcp 0 50 10.10.1.1:52653 10.10.1.200:3001 ESTABLISHED(建立) tcp 0 0 10.10.1.1:52624 10.10.1.200:3001 FIN_WAIT_1 tcp 0 0 10.20.1.1:61704 10.20.1.3:48879 ESTABLISHED(建立) tcp 0 0 *:3001 *:* LISTEN tcp 0 0 *:3002 *:* LISTEN tcp 0 0 *:3003 *:* LISTEN(听) tcp 0 0 *:4000 *:* LISTEN tcp 0 0 172.16.124.217:4000 172.16.124.78:1024 ESTABLISHED(建立) tcp 0 0 172.16.124.217:4000 172.16.124.227:1025 ESTABLISHED(建立) tcp 0 0 *:4001 *:* LISTEN tcp 0 0 *:4002 *:* LISTEN tcp 0 0 *:4003 *:* LISTEN(听) tcp 0 0 *:4004 *:* LISTEN(听) tcp 0 0 *:22 *:* LISTEN(听) tcp 0 0 *:4005 *:* LISTEN(听) tcp 0 0 *:4006 *:* LISTEN(听) tcp 0 0 172.16.124.217:4006 172.16.124.203:49231 ESTABLISHED(建立) tcp 0 0 *:4007 *:* LISTEN(听) tcp 0 0 *:4008 *:* LISTEN(听) tcp 0 0 *:4009 *:* LISTEN(听) tcp 0 0 172.16.124.217:4008 172.16.124.203:49262 ESTABLISHED(简体中文) tcp 0 0 *:4010 *:* LISTEN tcp 0 0 *:4011 *:* LISTEN tcp 0 0 *:4012 *:* LISTEN tcp 0 0 *:4013 *:* LISTEN tcp 0 0 *:4014 *:* LISTEN tcp 0 0 *:4015 *:* LISTEN tcp 0 0 *:80 *:* LISTEN tcp 0 0 *:9182 *:* LISTEN(倾听) tcp 0 0 *:445 *:* LISTEN(听) tcp 0 0 *:139 *:* LISTEN tcp 0 0 10.20.1.1:53495 10.20.1.9:48879 ESTABLISHED(建立) tcp 0 0 10.20.1.1:61703 10.20.1.3:48879 ESTABLISHED(建立) tcp 0 0 10.20.1.1:61707 10.20.1.3:48879 ESTABLISHED(建立) tcp 0 0 10.20.1.1:61705 10.20.1.9:48879 ESTABLISHED(建立) tcp 0 0 10.20.1.1:61709 10.20.1.9:48879 ESTABLISHED(建立) tcp 0 0 10.20.1.1:61710 10.20.1.9:48879 ESTABLISHED(建立) tcp 0 0 172.16.124.217:61789 172.16.124.203:4000 ESTABLISHED(建立) tcp 0 400 172.16.124.217:22 172.16.124.50:17674 ESTABLISHED(建立) |
如果你数一下行数,你会看到超过42行,那是因为netstat显示的条目不是每一个都使用stcp设备克隆。例如,OSL连接和与NIO一起使用的X25_cpc连接。更多细节请看socket_count.cm。
如果在没有连接或绑定的情况下进行套接字调用,或者绑定失败,就会产生相反的问题,clone_count的值大于netstat显示的条目数。
作为:match clone; dump_dvt -name stcp.m17。
clone_limit: 5120
clone_count: 4131
cloned_from: -23179
远程克隆限制: 0
作为。
|
我没有再包括netstat的输出,但相信我,它与之前的例子没有变化。
这种情况下,一个额外的(4131 - 42)和显然未被说明的STCP设备克隆是由以下代码片段造成的。该代码调用socket函数,然后是bind函数。如果绑定失败就会循环。许多应用程序会添加一个定时器,等待1、60或300秒再试,那只是拖延了不可避免的时间,当然,假设导致错误的条件不会消失。
tryAgain = 1; while (tryAgain) { if ((socks0 = socket (AF_INET, SOCK_STREAM, 0)) < 0) { if (debugFlag) perror ("badService: can't create listening socket"); } else { /* build a sockaddr structure holding the address we will bind to. The IP address is INADDR_ANY meaning we will listen on all active IP addresses */ bzero ( (char *) &serv_addr, sizeof (serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl (INADDR_ANY); serv_addr.sin_port = htons (portNumber); /* now bind to the address and port */ if (bind (socks0, (struct sockaddr *) &serv_addr, sizeof (serv_addr)) < 0) { if (debugFlag) perror ("badService: can't bind address, trying again"); } else tryAgain = 0; } } |
最常见的错误是另一个进程已经绑定到了请求的端口。无论错误的原因是什么,解决办法是在报告绑定错误后关闭套接字。
tryAgain = 1;
while (tryAgain)
{
if ((socks0 = socket (AF_INET, SOCK_STREAM, 0)) < 0)
{
if (debugFlag)
perror ("goodService: can't create listening socket");
}
else {
/* build a sockaddr structure holding the address we will bind to.
The IP address is INADDR_ANY meaning we will listen on all active
IP addresses */
bzero ( (char *) &serv_addr, sizeof (serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl (INADDR_ANY);
serv_addr.sin_port = htons (portNumber);
/* now bind to the address and port */
if (bind (socks0, (struct sockaddr *) &serv_addr,
sizeof (serv_addr)) < 0)
{
if (debugFlag)
perror ("goodService: can't bind address, trying again");
if (close (socks0) < 0)
if (debugFlag)
perror ("goodService: can't close old socket");
}
else
tryAgain = 0;
}
}
|
这个帖子一直在讲因为编码错误而达到clone_limit,但是如果没有错误呢,如果应用环境真的使用了所有这些clone设备呢。那么,假设你没有达到16000的系统限制,你可以提高限制。你需要更新devices.tin文件中stcp设备的clone_limit字段,并重新创建devices.table。如果你是在17.1或更高版本上,你可以使用update_device_info命令来提高当前启动的限制,并依靠更新的devices.table来处理下一次启动。在17.1之前的版本中,你唯一的选择就是重新启动。你应该将限制设置为一个符合你当前需求和预期增长的值;你不应该仅仅将限制提高到16,000。即使你现在没有消耗克隆设备的应用程序bug,也不能保证将来不会有。一个应用如果消耗了所有可用的克隆设备,也会消耗大量的流内存,耗尽流内存会对现有的TCP连接产生负面影响。