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.
