94 votes

Socket accept - "Trop de fichiers ouverts"

Je suis en train de travailler sur un projet d'école, où j'ai eu à écrire un serveur multithread, et maintenant je suis en la comparant à d'apache par l'exécution de certains tests contre elle. Je suis à l'aide de autobench pour aider avec cela, mais après j'ai exécuter quelques tests, ou si je lui donne un trop haut niveau de taux (autour de 600+) pour faire le lien, j'obtiens un "Trop de fichiers ouverts" erreur.

Après je suis fait traiter d'une demande, je fais toujours un close() sur le socket. J'ai essayé d'utiliser l' shutdown() de la fonction en tant que bien, mais rien ne semble aider. Moyen de contourner ce problème?

89voto

Reed Copsey Points 315315

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

Vous pouvez vérifier les points suivants:

cat /proc/sys/fs/file-max

Qui vous donnera un vaste système de limites de descripteurs de fichiers.

Sur la coque, cela va vous dire que votre limite personnelle:

ulimit -n

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

Toutefois, si vous êtes à la clôture de vos prises de courant correctement, vous ne devriez pas recevoir cela, sauf si vous êtes à l'ouverture d'un lot de simulataneous connexions. Cela ressemble à quelque chose, c'est de prévenir vos prises de courant à partir de la fermeture de façon appropriée. Je voudrais vérifier s'ils sont traités correctement.

57voto

Nick Points 662

J'ai eu le même problème. Solution rapide est :

ulimit -n 4096

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

Note ce a pas beaucoup de relation avec le système de fichiers max (/proc/sys/fs/file-max).

Dans mon cas, il a été le problème avec le Redis, j'ai donc fait:

ulimit -n 4096
redis-server -c xxxx

dans votre cas, au lieu de le redis, vous avez besoin pour démarrer votre serveur.

17voto

Ed4 Points 603

TCP a une fonction appelée "TIME_WAIT" qui assure les connexions sont fermées proprement. Il nécessite une extrémité de la connexion à rester à l'écoute pendant un certain temps après la prise a été fermé.

Dans un serveur de haute performance, il est important que ce sont les clients qui vont dans TIME_WAIT, pas sur le serveur. Les Clients peuvent se permettre d'avoir un port ouvert, tandis qu'un serveur occupé pouvez rapidement à court de ports ou qui ont trop ouvert les FDs.

Pour ce faire, le serveur ne doit jamais fermer la connexion d'abord, il faut toujours attendre le client pour le fermer.

6voto

Rafael Baptista Points 3884

J'ai eu ce problème aussi. Vous avez une fuite de handles de fichier. Vous pouvez déboguer ce par l'impression d'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 << ") ";
   }
}

Par le dumping de tous les fichiers ouverts, vous allez rapidement comprendre où votre fichier fuite de handle est.

Si votre serveur engendre les sous-processus. E. g. si c'est un "fork" de style serveur, ou si vous êtes à la ponte d'autres processus ( par exemple, via cgi ), vous devez assurez-vous de créer votre fichier de poignées avec "cloexec" - à la fois pour les fichiers réels et aussi les sockets.

Sans cloexec, à chaque fois que vous fourche ou se reproduit, tous les descripteurs de fichiers ouverts sont clonés dans le processus de l'enfant.

Il est très facile aussi de ne parviennent pas à fermer les sockets réseau - par exemple, juste de les abandonner lorsque la partie à distance se déconnecte. Cela permettra de fuite de handles comme un fou.

4voto

Partly Cloudy Points 855

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X