2010-07-07 2 views
5

J'écris une application en C pour linux, qui utilise 2 bibliothèques tierces séparées. Les deux bibliothèques sont asynchrones et utilisent select(). Ils fournissent également une API qui renvoie les descripteurs de fichiers qu'ils attendent. Mon intention est de les passer dans mon propre select(), puis de ramener le contrôle à la bibliothèque, quelle que soit la valeur de leur fd.appel unix select(): comment combiner fd_sets?

Je pense que j'ai écrit la plupart, mais j'ai des problèmes au niveau des paramètres select(): Les deux bibliothèques ne donnent pas de descripteurs de fichiers individuels, mais des pointeurs sur leurs fd_sets en lecture et en écriture. J'ai besoin de combiner les fd_sets retournés de ces bibliothèques dans un fd_set pour la lecture, un fd_set pour l'écriture, etc.

Des suggestions sur la façon dont je peux combiner 2 fd_sets en un résultat fd_set?

Addendum Désolé! J'aurais dû être plus clair ... ces librairies ne renvoient que les fd_sets ... Je ne connais pas le nombre de FDs dans chaque set pour que je puisse faire une boucle for et régler chaque FD individuellement .. y at-il un moyen simple de déterminer cela donné juste un fd_set?

+0

* Je suis en train d'écrire une application en C pour Linux, qui utilise 2 séparées bibliothèques tierces. * Devrait * Je suis en train d'écrire une application en C pour Linux qui utilise deux bibliothèques tierces. * – mcandre

+0

Etes-vous sûr que cette stratégie va fonctionner? Généralement, ces bibliothèques implémentent leur propre boucle d'événements et attendent que vous configuriez des rappels et que vous leur transfériez le contrôle exclusivement. Les bons vous permettront d'ajouter des descripteurs de fichiers de votre choix à leurs listes de sélection. Dans un programme à un seul thread, vous ne pouvez pas avoir facilement 2 boucles d'événements de contrôle exclusives. – bjg

Répondre

1

Vous pouvez configurer votre propre fd_set en faisant une boucle sur tous les descripteurs de fichiers possibles que chaque bibliothèque pourrait éventuellement ouvrir et dans chacun de leurs ensembles retournés et définissez les vôtres en conséquence. C'est un peu brutal mais l'interface select est malheureusement assez primitive. Par exemple

fd_set *lib1_read_fds, *lib2_read_fds; 
int fd; 
fdset my_fd_set; 
FD_CLR(&my_fd_set); 
for (fd = 0; fd < FD_SETSIZE; fd++) { 
    if (FD_ISSET(fd, lib1_read_fds) || FD_ISSET(fd, lib2_read_fds)) { 
     FD_SET(fd, &my_fd_set); 
    } 
} 
+0

Vous devez utiliser FD_ZERO, pas FD_CLR pour mettre à zéro le jeu de résultats. – JeremyP

+0

Mon mauvais. Devrait être FD_ZERO bien sûr. – bjg

0

Le fd_set est en fait un bitmap. Peut-être que vous pouvez les combiner en utilisant binaire ou.

typedef long int __fd_mask; 

typedef struct 
{ 
    __fd_mask __fds_bits[__FD_SETSIZE/__NFDBITS]; 
} fd_set; 
+0

Il faudra peut-être faire particulièrement attention à la taille de fd_set: elle peut également être allouée dynamiquement. Accéder à des octets après le '(nfds + 7)/8' (où' nfds' est l'argument du premier select()) doit être évité. À moins bien sûr que l'on puisse confirmer que les bibliothèques utilisent réellement le bon fd_set de sys/select.h. – Dummy00001

+1

Sauf dans les applications les plus exigeantes en performance, je recommande vivement de ne pas utiliser cette approche. Regarder à l'intérieur de structures de données opaques et écrire du code qui dépend de leur implémentation est une très mauvaise pratique. –

1

Sans compter sur la mise en œuvre interne de fd_set, le meilleur que vous pouvez faire est une simple paire de boucles

fd_set combine_sets(fd_set* p_set1, int n1, fd_set* p_set2, int n2) 
{ 
    fd_set combined; 
    int i; 

    FD_ZERO(combined); 

    for (i = 0; i < n1; ++i) { 
    if (FD_ISSET(i, p_set1)) { 
     FD_SET(i, &combined); 
    } 
    } 

    for (i = 0; i < n2; ++i) { 
    if (FD_ISSET(i, p_set2)) { 
     FD_SET(i, &combined); 
    } 
    } 

    return combined; 
} 

Lorsque les n paramètres sont le plus grand nombre de descripteurs potentiellement dans l'ensemble correspondant, plus un (c'est-à-dire l'argument nfds que vous passeriez à select si vous utilisiez seulement cet ensemble).

Editer: Modification de prendre des pointeurs comme arguments, puisque c'est ce que vous avez dit que vous obtenez de vos bibliothèques.

+0

Retourner un fd_set semble être une très mauvaise idée. Cela fait longtemps que je n'ai pas lu POSIX mais cela peut même permettre à fd_set d'être un type tableau, auquel cas ce code échouerait. Mieux vaut passer un pointeur sur un fd_set et laisser la fonction écrire dedans ... –

1

par un jeu Itérer et vérifiez chaque fd jusqu'à FD_SETSIZE avec FD_ISSET() et si elle est définie, réglez le fd dans l'autre jeu à l'aide FD_SET()

0

Je ne pense pas que vous pouvez le faire portably , si vous réécrivez votre code pour utiliser poll() vous pouvez fusionner les ensembles qu'il utilise, car ils ne sont que des tableaux de portably struct s

5
Code

C qui ne dépend pas de la mise en œuvre de fd_set:

void Fdset_Add(fd_set *Out, fd_set const *In, int InNfds) 
{ 
    for(i = 0; i < InNfds; i++) 
    { 
     if(i < InNfds && FD_ISSET(i, In)) 
      FD_SET(i, Out); 
    } 
} 

int Fdset_Merge(fd_set *Out, fd_set const *In1, int NFds1, fd_set const *In2, int NFds2) 
{ 
    FD_ZERO(Out); 
    Fdset_Add(Out, In1, Nfds1); 
    Fdset_Add(Out, In2, Nfds2); 
    return Nfds1 > Nfds2 ? Nfds1 : Nfds2; 
} 

int Fdset_Filter(fd_set const *Result, int ResultNfds, fd_set *ToFilter, int NfdsToFilter) 
{ 
    int i; 
    int Retval; 

    Retval = 0; 
    for(i = 0; i < ResultNfds; i++) 
    { 
     if(i < NfdsToFilter && FD_ISSET(i, ToFilter)) 
     { 
      if(! FD_ISSET(i, Result)) 
       FD_CLR(i, ToFilter); 
      else 
       Retval++; 
     } 
    } 
    return Retval; 
} 

void Fdset_Split(fd_set const *Result, int ResultNfds, fd_set *In1, int Nfds1, int *Count1, fd_set *In2, int Nfds2, int *Count2) 
{ 
    *Count1 = Fdset_Filter(Result, ResultNfds, In1, Nfds1); 
    *Count2 = Fdset_Filter(Result, ResultNfds, In1, Nfds2); 
} 
+0

dans FDSet_Merge() vous ne faites que passer au minimum de NFds1 et NFds2. Vous devez boucler au maximum des deux – JeremyP

+0

@JeremyP - merci, corrigé. –

+0

Cela ne devrait-il pas être "const fd_set * In", et ainsi de suite? – unwind

Questions connexes