[LINUX] fd_set de select (2) tombe en essayant de définir fd de 1024 ou plus

introduction

En fonction de la description sur le net, il est souvent écrit que select ne peut surveiller que jusqu'à 1024 FD car l'argument fd_set n'accepte que 1024 fds, mais en réalité c'est ** 1024 ou plus. fd ne sera pas accepté **. Même s'il en est un, il sera supprimé si vous essayez de FD_SET un nombre de 1024 ou plus. C'est tout pour la conclusion, mais j'aimerais suivre le contenu.

Si vous regardez attentivement, vous pouvez également le trouver dans select man page.

fd_set est un tampon de taille fixe. Ce qui se passe lorsque FD_CLR () ou FD_SET () est exécuté sur un fd négatif ou dont la valeur est supérieure ou égale à FD_SETSIZE n’est pas défini.

Il y a une déclaration d'action de peur indéfinie.

select(2) Un appel système qui surveille les descripteurs de fichier et les descripteurs de socket et ne sait généralement pas quand un appel entrant arrive, comme un programme serveur, est un appel système couramment utilisé pour écouter un appel entrant.

int select(int nfds, fd_set *readfds, fd_set *writefds,
           fd_set *exceptfds, struct timeval *timeout);

Mis à part les détails, grosso modo, c'est un appel système qui surveille le fd (descripteur de fichier) enregistré dans fd_set. En liant select (2) à read (2), les données peuvent être lues plus efficacement et pilotées par les événements que lorsque la lecture est en attente. C'est comme le créateur de boost :: asio :: async_read.

fd_set et FD_SET

Comme son nom l'indique, fd_set est une structure qui représente un ensemble de fd. En regardant mon sys / select.h (assez omis)

typedef long int __fd_mask;
# define __FD_SETSIZE 1024
# define __NFDBITS (8 * (int)sizeof(__fd_mask));

typedef struct
  {
    __fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];
# define __FDS_BITS(set) ((set)->fds_bits)
  } fd_set;

Si vous le lisez, s'il s'agit d'une version 64 bits de Linux, la longueur est 64 bits, donc

typedef struct {
    long int fds_bits[16];
} fd_set;

Par conséquent, à la fin, cela devient une structure avec une zone de 1024 bits (64 bits * 16). Et c'est la macro FD_SET qui définit le fd inclus ici.

#define	__FD_ELT(d) ((d) / __NFDBITS)
#define	__FD_MASK(d) ((__fd_mask) (1UL << ((d) % __NFDBITS)))
#define __FD_SET(d, set) ((void) (__FDS_BITS (set)[__FD_ELT (d)] |= __FD_MASK (d)))
#define	FD_SET(fd, fdsetp) __FD_SET (fd, fdsetp)

C'est un peu difficile à comprendre, donc si vous résolvez la macro,

fdsetp->fds_bits[fd/64] |= (__fd_mask) (1UL << (fd % 64));

En bref, il s'agit de définir le bit du chiffre fd de fdsetp (pointeur de variable de type fd_set).

Comprenez vous?

C'est vrai. Au moment où fd dépasse 1024, il tombe en raison d'un débordement de tampon. De plus, FD_SETSIZE est codé en dur avec define et ne peut pas être changé car c'est effrayant. Redhat QA page indique également que FD_SETSIZE ne doit pas être réécrit. La limite supérieure de ʻulimitest souvent 1024 même dans les distributions majeures modernes, mais je pense que c'est un réglage naturel pour les auteurs de programmes serveur de supprimer la limite supérieure avec ulimit. Surtout si vous utilisez docker, vous pouvez facilement supprimer la restriction en option. Cependant, si la bibliothèque que vous utilisez dans votre programme est implémentée avecselect (2)`, elle tombera de manière inattendue!

Résumé

Si vous utilisez un descripteur dans un programme qui utilise select (2) pour supprimer la limite de ʻulimit, il échouera avec FD_SET. En fait, j'ai rencontré ce problème lors de l'écriture d'un programme en utilisant une bibliothèque d'un certain protocole NW. A partir de maintenant, ceux qui écrivent des programmes NW basse couche devraient éviter d'utiliser select (2). Veuillez utiliser poll (2)`!

Recommended Posts

fd_set de select (2) tombe en essayant de définir fd de 1024 ou plus
Erreur lors de la tentative d'installation de psycopg2 en Python
UnicodeEncodeError lors de la tentative d'exécution du radon