2008-11-12 24 views
6
while (xxx) { 
    timeout.tv_sec=TIMEOUT; 
    timeout.tv_usec=0; 
    FD_ZERO(&set); 
    FD_SET(sd,&set); 

    switch (select(FD_SETSIZE,&set,NULL,NULL,&timeout)) 
    xxxxx 
} 

fonctionne très bien, maisirritant select() comportement c

FD_ZERO(&set); 
FD_SET(sd,&set); 

while (xxx) { 
    timeout.tv_sec=TIMEOUT; 
    timeout.tv_usec=0; 

    switch (select(FD_SETSIZE,&set,NULL,NULL,&timeout)) 
    xxxxx 
} 

ne fonctionne pas. Cela fonctionne la première fois, mais la prochaine fois qu'il parcourt la boucle while, il obtient un timeout même si le socket sd reçoit des données. Il me semble être un gaspillage de ressources à devoir vider et remplir à chaque fois.

Quelqu'un a une bonne explication pourquoi c'est, et même mieux, peut-être une suggestion pour l'éviter?

+0

Je pense qu'une partie du code que vous avez retiré peut être important pour comprendre pourquoi cela fonctionne comme il est. – SoapBox

Répondre

12

select modifie ses arguments. Vous devez vraiment le réinitialiser à chaque fois.

Si vous êtes préoccupé par les frais généraux, le coût du traitement du FD_SET complet dans le noyau est un peu plus important que le coût de FD_ZERO. Vous ne voulez transmettre que le maximum de votre fd, pas FD_SETSZIZE, pour minimiser le traitement du noyau. Dans votre exemple:

switch (select((sd + 1),&set,NULL,NULL,&timeout)) 

Pour un cas plus complexe avec plusieurs fds, vous finissent généralement maintenir une variable max:

FD_SET(sd,&set); 
if (sd > max) max = sd; 
... repeat many times... 

switch (select((max + 1),&set,NULL,NULL,&timeout)) 


Si vous avez un grand nombre de descripteurs de fichiers et sont préoccupé par les frais généraux de les tromper, vous devriez regarder certaines des alternatives à sélectionner(). Vous ne mentionnez pas le système d'exploitation que vous utilisez, mais pour les systèmes d'exploitation Unix il y a quelques:

  • pour Linux, epoll()
  • pour FreeBSD/NetBSD/OpenBSD/Mac OS X, kqueue ()
  • pour Solaris,/dev/poll

Les API sont différents, mais ils sont essentiellement une interface de noyau stateful pour maintenir un ensemble de descriptions de fichiers actifs. Une fois qu'un fd est ajouté à l'ensemble, vous serez averti des événements sur ce fd sans avoir à le transmettre de nouveau en permanence.

+0

Merci pour une excellente réponse :) – deadcyclo

+1

Pour une alternative à select(), pourquoi ne pas poll() qui, contrairement à ceux que vous mentionnez, est la même sur beaucoup d'Unices? – bortzmeyer

+1

poll() est meilleur car vous n'avez pas besoin de réinitialiser son tableau à chaque fois, mais vous continuez à copier une grande structure dans et hors du noyau à chaque appel. Si vous avez beaucoup de fds, les autres alternatives évitent cette surcharge constante en modifiant l'ensemble fd dans le noyau. – DGentry

7

Lisez la page de manuel de sélection. L'ensemble renvoyé est uniquement les descripteurs de fichier prêts à être utilisés. Vous êtes supposé utiliser FD_ISSET pour vérifier chacun d'eux s'il est activé ou non.

Toujours initialiser le fd_set juste avant de l'utiliser.

+0

Je connais le FD_ISSET, mais je ne l'ai pas encore inclus, car pour l'instant je ne fais que lister un socket (j'ajouterai plus de sockets plus tard). Donc il est maintenant possible de "réutiliser" sans avoir à mettre fd_set en premier? – deadcyclo

+0

Le comportement documenté de select() inclut la modification des ensembles en place. Pour citer The Pragmatic Programmer, "'select' n'est pas cassé". – bk1e

0

C'est ainsi que fonctionne Select. Cela fonctionne mieux, et a plus de sens, si vous avez plus d'un socket. C'est un peu le point: vous choisissez parmi de nombreuses interfaces. Si vous voulez lire à partir d'une socket, il suffit de lire ou recv.

+1

Read n'offre pas de délai d'expiration. D'où l'utilisation de select. + le fait que j'ajouterai un second socket plus tard dans la phase de développement. – deadcyclo

+0

Juste pour continuer à offrir des alternatives: si sd est un socket, vous pouvez utiliser setsockopt (sd, SO_RCVTIMEO, ...) pour ajouter un timeout en lecture.Cependant si vous allez ajouter un second socket plus tard, sélectionnez est la meilleure option à poursuivre. – DGentry