跳转至主要内容

在这篇文章中,我希望讨论一个常见的编码错误,它可能会导致一个模块在创建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连接产生负面影响。

© 2024Stratus Technologies.