Passer au contenu principal

Dans ce billet, je souhaite discuter d'une erreur de codage courante qui peut entraîner l'épuisement ou, du moins, l'épuisement sérieux du nombre de clones de périphériques stcp disponibles pour la création de sockets TCP.

Chaque appel à la fonction socket entraîne la création par STCP d'un nouveau clone du périphérique stcp. Le nombre maximal de clones pouvant être créés est contrôlé par le champ clone_limit dans l'entrée devices.tin.

/    =nom               stcp.m17
     =nom_module        m17
     =type_périphérique        flux
     =nom_liste_accès   stcp_access
     =pilote_flux     stcp
     =clone_limit        5120
     =comment            Fournit l'API TCP

Vous pouvez voir combien de clones sont actuellement utilisés en vidant la structure du périphérique dans analyze_system et en consultant la valeur clone_count. Si clone_count est égal à clone_limit, les appels à la fonction socket renverront une erreur e$clone_limit_exceeded : « La limite de clones pour le périphérique a été dépassée ».

comme :  match clone ; dump_dvt -name stcp.m17
clone_limit :       5120
clone_count :       42
cloned_from :       -27271
remote_clone_limit : 0

En général, l'appel de socket qui crée le périphérique cloné est suivi soit d'un connect ou bind et listen . À ce stade, vous pouvez voir une entrée correspondante lorsque vous exécutez la commande « netstat ».

netstat -numeric -all_sockets
Connexions actives (y compris les serveurs)
Proto Recv-Q Send-Q Adresse locale Adresse étrangère (état)
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               *:*                LISTEN
tcp        0      0  10.20.1.1:37       10.20.1.9:3633     TIME_WAIT
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             *:*                ÉCOUTE
tcp        0      0  172.16.124.217:4006 172.16.124.203:49231 ÉTABLI
tcp        0      0  *:4007             *:*                ÉCOUTE
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              *:*                ÉCOUTE
tcp        0      0  *:139              *:*                ÉCOUTE
tcp        0      0  10.20.1.1:53495    10.20.1.9:48879    ÉTABLI
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    ÉTABLI
tcp        0      0  10.20.1.1:61709    10.20.1.9:48879    ÉTABLI
tcp        0      0  10.20.1.1:61710    10.20.1.9:48879    ÉTABLI
tcp        0      0  172.16.124.217:61789 172.16.124.203:4000 ÉTABLI
tcp        0    400  172.16.124.217:22  172.16.124.50:17674 ESTABLISHED

Si vous comptez le nombre de lignes, vous verrez qu'il y en a plus de 42, car toutes les entrées affichées par netstat n'utilisent pas un clone de périphérique stcp. Par exemple, les connexions OSL et les connexions X25_cpc utilisées avec le NIO. Consultez socket_count.cm pour plus de détails.

Si un appel socket est effectué sans connexion ou liaison, ou si la liaison échoue, vous pouvez créer le problème inverse : la valeur de clone_count est supérieure au nombre d'entrées affiché par netstat.

comme :  clone de correspondance ; dump_dvt -nom stcp.m17
limite de clonage :       5120
clone_count :       4131
cloned_from :       -23179
remote_clone_limit : 0
as :

Je ne reproduis pas ici la sortie netstat, mais croyez-moi, elle n'a pas changé par rapport à l'exemple précédent.

Cette situation, à savoir un clone supplémentaire (4131 – 42) et apparemment non pris en compte, du périphérique STCP a été créée par le fragment de code suivant. Le code appelle la fonction socket suivie de la fonction bind. Si la liaison échoue, il boucle. De nombreuses applications ajouteraient un minuteur pour attendre 1, 60 ou 300 secondes et réessayer, ce qui ne fait que retarder l'inévitable, en supposant bien sûr que la condition à l'origine de l'erreur ne disparaisse pas.

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;
     }
   }

L'erreur la plus courante est qu'un autre processus est déjà lié au port demandé. Quelle que soit la raison de l'erreur, la solution consiste à fermer le socket après avoir signalé l'erreur de liaison.

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;
     }
   }

 

Cet article traitait de l'atteinte de la limite clone_limit en raison d'une erreur de codage, mais que se passe-t-il s'il n'y a pas d'erreur, si l'environnement d'application utilise réellement tous ces périphériques clones ? Dans ce cas, en supposant que vous n'ayez pas atteint la limite système de 16 000, vous pouvez augmenter la limite. Vous devez mettre à jour le champ clone_limit du périphérique stcp dans le fichier devices.tin et recréer le fichier devices.table. Si vous utilisez une version 17.1 ou ultérieure, vous pouvez utiliser la commande update_device_info pour augmenter la limite pour le démarrage actuel et vous fier au fichier devices.table mis à jour pour le prochain démarrage. Sur les versions antérieures à 17.1, votre seule option réelle est de redémarrer. Vous devez définir la limite à une valeur qui correspond à vos besoins actuels et à la croissance prévue ; vous ne devez pas simplement augmenter la limite à 16 000. Même si vous n'avez pas actuellement de bogue d'application qui consomme des périphériques clones, rien ne garantit que vous n'en aurez pas à l'avenir. Une application qui consomme tous les périphériques clones disponibles consommera également une grande quantité de mémoire de flux, ce qui aura un impact négatif sur les connexions TCP existantes.