web-dev-qa-db-fra.com

Socket accept - "Trop de fichiers ouverts"

Je travaille sur un projet d'école où je devais écrire un serveur multi-thread, et maintenant je le compare à Apache en exécutant quelques tests contre lui. J'utilise Autobench pour aider à cela, mais après avoir exécuté quelques tests, ou si je lui donne un taux trop élevé (environ 600+) pour établir les connexions, j'obtiens une erreur "Trop de fichiers ouverts".

Après avoir fini de traiter la requête, je fais toujours une close() sur le socket. J'ai également essayé d'utiliser la fonction shutdown(), mais rien ne semble aider. Un moyen de contourner cela?

53
Scott

Il existe plusieurs endroits où Linux peut avoir des limites sur le nombre de descripteurs de fichiers que vous êtes autorisé à ouvrir.

Vous pouvez vérifier les éléments suivants:

cat /proc/sys/fs/file-max

Cela vous donnera les limites du système de descripteurs de fichiers.

Au niveau Shell, cela vous indiquera votre limite personnelle:

ulimit -n

Cela peut être changé dans /etc/security/limits.conf - c'est le paramètre nofile.

Cependant, si vous fermez correctement vos sockets, vous ne devriez pas recevoir cela à moins que vous n'ouvriez beaucoup de connexions simultanées. Il semble que quelque chose empêche vos prises de se fermer correctement. Je voudrais vérifier qu'ils sont traités correctement.

55
Reed Copsey

J'ai eu un problème similaire. La solution rapide est:

ulimit -n 4096

l'explication est la suivante - chaque connexion au serveur est un descripteur de fichier. Dans CentOS, Redhat et Fedora, probablement d'autres, la limite d'utilisateurs de fichiers est de 1024 - aucune idée pourquoi. Il peut être facilement vu lorsque vous tapez: ulimit -n

Notez que cela n'a pas beaucoup de rapport avec les fichiers max du système (/ proc/sys/fs/file-max).

Dans mon cas, c'était un problème avec Redis, alors j'ai fait:

ulimit -n 4096
redis-server -c xxxx

dans votre cas au lieu de redis, vous devez démarrer votre serveur.

26
Nick

TCP a une fonctionnalité appelée "TIME_WAIT" qui garantit que les connexions sont fermées proprement. Cela nécessite une extrémité de la connexion pour rester à l'écoute pendant un certain temps après la fermeture du socket.

Dans un serveur hautes performances, il est important que ce soient les clients qui accèdent à TIME_WAIT, pas le serveur. Les clients peuvent se permettre d'avoir un port ouvert, tandis qu'un serveur occupé peut rapidement manquer de ports ou avoir trop de FD ouverts.

Pour ce faire, le serveur ne doit jamais fermer la connexion en premier - il doit toujours attendre que le client la ferme.

15
Ed4

Utilisation lsof -u `whoami` | wc -l pour trouver le nombre de fichiers ouverts dont dispose l'utilisateur

10
Edson Medina

Cela signifie que le nombre maximum de fichiers ouverts simultanément.

Résolu:

À la fin du fichier /etc/security/limits.conf, Vous devez ajouter les lignes suivantes:

* soft nofile 16384
* hard nofile 16384

Dans la console actuelle de root (Sudo ne fonctionne pas) pour faire:

ulimit -n 16384

Bien que cela soit facultatif, s'il est possible de redémarrer le serveur.

Dans le fichier /etc/nginx/nginx.conf Pour enregistrer la nouvelle valeur worker_connections Égale à 16384 Divisez par la valeur worker_processes.

Si ce n'est pas le cas, ulimit -n 16384, Vous devez redémarrer, le problème disparaîtra.

PS:

Si après la réparation est visible dans les journaux error accept() failed (24: Too many open files):

Dans la configuration nginx, propevia (par exemple):

worker_processes 2;

worker_rlimit_nofile 16384;

events {
  worker_connections 8192;
}
7
shilovk

J'ai eu ce problème également. Vous avez une fuite de descripteur de fichier. Vous pouvez déboguer cela en imprimant une liste de tous les descripteurs de fichiers ouverts (sur les systèmes POSIX):

void showFDInfo()
{
   s32 numHandles = getdtablesize();

   for ( s32 i = 0; i < numHandles; i++ )
   {
      s32 fd_flags = fcntl( i, F_GETFD ); 
      if ( fd_flags == -1 ) continue;


      showFDInfo( i );
   }
}

void showFDInfo( s32 fd )
{
   char buf[256];

   s32 fd_flags = fcntl( fd, F_GETFD ); 
   if ( fd_flags == -1 ) return;

   s32 fl_flags = fcntl( fd, F_GETFL ); 
   if ( fl_flags == -1 ) return;

   char path[256];
   sprintf( path, "/proc/self/fd/%d", fd );

   memset( &buf[0], 0, 256 );
   ssize_t s = readlink( path, &buf[0], 256 );
   if ( s == -1 )
   {
        cerr << " (" << path << "): " << "not available";
        return;
   }
   cerr << fd << " (" << buf << "): ";

   if ( fd_flags & FD_CLOEXEC )  cerr << "cloexec ";

   // file status
   if ( fl_flags & O_APPEND   )  cerr << "append ";
   if ( fl_flags & O_NONBLOCK )  cerr << "nonblock ";

   // acc mode
   if ( fl_flags & O_RDONLY   )  cerr << "read-only ";
   if ( fl_flags & O_RDWR     )  cerr << "read-write ";
   if ( fl_flags & O_WRONLY   )  cerr << "write-only ";

   if ( fl_flags & O_DSYNC    )  cerr << "dsync ";
   if ( fl_flags & O_RSYNC    )  cerr << "rsync ";
   if ( fl_flags & O_SYNC     )  cerr << "sync ";

   struct flock fl;
   fl.l_type = F_WRLCK;
   fl.l_whence = 0;
   fl.l_start = 0;
   fl.l_len = 0;
   fcntl( fd, F_GETLK, &fl );
   if ( fl.l_type != F_UNLCK )
   {
      if ( fl.l_type == F_WRLCK )
         cerr << "write-locked";
      else
         cerr << "read-locked";
      cerr << "(pid:" << fl.l_pid << ") ";
   }
}

En vidant tous les fichiers ouverts, vous découvrirez rapidement où se trouve la fuite de la poignée de votre fichier.

Si votre serveur génère des sous-processus. Par exemple. s'il s'agit d'un serveur de style 'fork', ou si vous générez d'autres processus (par exemple via cgi), vous devez vous assurer de créer vos descripteurs de fichiers avec "cloexec" - à la fois pour les vrais fichiers et aussi pour les sockets.

Sans cloexec, chaque fois que vous effectuez un fork ou un spawn, tous les descripteurs de fichiers ouverts sont clonés dans le processus enfant.

Il est également très facile de ne pas fermer les sockets réseau - par exemple les abandonnant simplement lorsque le correspondant distant se déconnecte. Cela fuira des poignées comme des fous.

6
Rafael Baptista

cela peut prendre un peu de temps avant qu'une prise fermée ne soit vraiment libérée

lsof pour lister les fichiers ouverts

cat /proc/sys/fs/file-max pour voir s'il y a une limite système

4
Partly Cloudy

J'ai eu le même problème et je n'ai pas pris la peine de vérifier les valeurs de retour des appels close (). Lorsque j'ai commencé à vérifier la valeur de retour, le problème a mystérieusement disparu.

Je ne peux que supposer un problème d'optimisation du compilateur (gcc dans mon cas), en supposant que les appels close () sont sans effets secondaires et peuvent être omis si leurs valeurs de retour ne sont pas utilisées.

1
dwk

Lorsque votre programme a plus de descripteurs ouverts que les fichiers ouverts ulimit (ulimit -a les listera), le noyau refusera d'ouvrir plus de descripteurs de fichiers. Assurez-vous que vous n'avez aucune fuite de descripteur de fichier - par exemple, en l'exécutant pendant un certain temps, puis en vous arrêtant et en voyant si des fds supplémentaires sont toujours ouverts lorsqu'il est inactif - et si c'est toujours un problème, modifiez l'ulimit nofile pour votre utilisateur dans /etc/security/limits.conf

1
bdonlan

Sur MacOS, montrez les limites:

launchctl limit maxfiles

Résultat comme: maxfiles 256 1000

Si les nombres (limite souple et limite stricte) sont trop bas, vous devez définir supérieur:

Sudo launchctl limit maxfiles 65536 200000
0
letanthang