Passa al contenuto principale

In questo post desidero discutere un errore di codifica comune che può causare l'esaurimento o almeno una grave riduzione del numero disponibile di cloni di dispositivi stcp utilizzabili per la creazione di socket TCP.

Ogni chiamata alla funzione socket comporta la creazione da parte di STCP di un nuovo clone del dispositivo stcp. Il numero massimo di cloni che possono essere creati è controllato dal campo clone_limit nella voce devices.tin.

/    =nome               stcp.m17
     =nome_modulo        m17
     =tipo_dispositivo        streams
     =nome_elenco_accessi   stcp_access
     =driver_streams     stcp
     =limite_cloni        5120
     =comment            Fornisce API TCP

È possibile vedere quanti cloni sono attualmente in uso scaricando la struttura del dispositivo in analyze_system e controllando il valore clone_count. Se clone_count è uguale a clone_limit, le chiamate alla funzione socket restituiranno un errore e$clone_limit_exceeded: "Il limite di cloni per il dispositivo è stato superato".

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

In generale, la chiamata socket che crea il dispositivo clone è seguita da un connect o da una chiamata bind e listen . A quel punto è possibile vedere una voce corrispondente quando si esegue il comando "netstat".

netstat -numeric -all_sockets
Connessioni attive (compresi i server)
Proto Recv-Q Send-Q Indirizzo locale Indirizzo esterno (stato)
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 STABILITO
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             *:*                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 STABILITO

Se conti il numero di linee, vedrai che sono più di 42, perché non tutte le voci mostrate da netstat usano un clone del dispositivo stcp. Per esempio, le connessioni OSL e le connessioni X25_cpc usate con NIO. Dai un'occhiata a socket_count.cm per maggiori dettagli.

Se viene effettuata una chiamata socket senza un connect o un bind, oppure se il bind fallisce, è possibile creare il problema opposto, ovvero il valore di clone_count è maggiore del numero di voci mostrato da netstat.

come:  match clone; dump_dvt -name stcp.m17
clone_limit:       5120
clone_count:       4131
cloned_from:       -23179
remote_clone_limit: 0
as:

Non includo nuovamente l'output di netstat, ma fidatevi, non è cambiato rispetto all'esempio precedente.

Questa situazione, un clone del dispositivo STCP in più (4131 – 42) e apparentemente non contabilizzato, è stata creata dal seguente frammento di codice. Il codice chiama la funzione socket seguita dalla funzione bind. Se il bind fallisce, entra in loop. Molte applicazioni aggiungerebbero un timer per attendere 1, 60 o 300 secondi e riprovare, ma questo non fa altro che ritardare l'inevitabile, supponendo ovviamente che la condizione che causa l'errore non scompaia.

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'errore più comune è che un altro processo si è già collegato alla porta richiesta. Indipendentemente dal motivo dell'errore, la soluzione è chiudere il socket dopo aver segnalato l'errore di collegamento.

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

 

Questo post riguardava il raggiungimento del limite clone_limit a causa di un errore di codifica, ma cosa succede se non c'è alcun errore, se l'ambiente dell'applicazione sta realmente utilizzando tutti quei dispositivi clone? Beh, supponendo che non sia stato raggiunto il limite di sistema di 16.000, è possibile aumentare il limite. È necessario aggiornare il campo clone_limit del dispositivo stcp nel file devices.tin e ricreare il file devices.table. Se si utilizza una versione 17.1 o successive, è possibile utilizzare il comando update_device_info per aumentare il limite per l'avvio corrente e affidarsi al file devices.table aggiornato per gestire il prossimo avvio. Nelle versioni precedenti alla 17.1, l'unica opzione reale è quella di riavviare il sistema. È necessario impostare il limite su un valore che corrisponda alle esigenze attuali più la crescita prevista; non è consigliabile aumentare il limite a 16.000. Anche se al momento non si riscontrano bug dell'applicazione che consumano dispositivi clone, non vi è alcuna garanzia che non se ne verificheranno in futuro. Un'applicazione che consuma tutti i dispositivi clone disponibili consumerà anche una grande quantità di memoria di flusso e l'esaurimento della memoria di flusso influirà negativamente sulle connessioni TCP esistenti.