2010-07-07 3 views
5

Sto scrivendo un'applicazione in C per linux, che utilizza 2 librerie di terze parti separate. Entrambe le librerie sono asincrone e usano select(). Forniscono inoltre un'API che restituisce i descrittori di file che aspettano. La mia intenzione è quella di passare questi in mio select() e quindi restituire il controllo a qualsiasi libreria quando sono impostati i loro valori fd.unix select() call: come combinare fd_sets?

Penso di averne scritto la maggior parte, ma ho problemi nel punto in cui i parametri select() sono interessati: Entrambe le librerie non forniscono singoli descrittori di file, ma puntatori alla loro lettura e scrittura di fd_sets. Ho bisogno di combinare il fd_set restituito da queste librerie in un fd_set per leggere, un fd_set per scrivere, ecc.

Qualche suggerimento su come posso combinare 2 fd_set in un fd_set risultante?

Addendum Siamo spiacenti! Avrei dovuto essere più chiaro .. queste librerie restituiscono solo gli fd_set ... Non conosco il numero di FD in ogni set in modo che possa fare un ciclo for e impostare ogni FD individualmente .. c'è un modo semplice per determinare questo dato solo un fd_set?

+0

* Sto scrivendo un programma in C per Linux, che utilizza 2 librerie di terze parti separate. * Dovrebbe essere * Sto scrivendo un programma in C per Linux che usa due librerie di terze parti. * – mcandre

+0

Sei sicuro che questa strategia funzionerà? In genere, tali librerie implementano il proprio ciclo di eventi e si aspettano di impostare i callback e il controllo del trasferimento su di essi esclusivamente. I buoni ti permetteranno di aggiungere i descrittori di file tuoi ai loro elenchi selezionati. In un programma a thread singolo non è facile avere 2 loop di eventi di controllo esclusivi, non importa 3. – bjg

risposta

1

È possibile impostare il proprio fd_set eseguendo il loop su tutti i possibili descrittori di file che ciascuna libreria potrebbe eventualmente aprire e in ciascuno dei set restituiti e impostare di conseguenza. È un po 'brute-force ma l'interfaccia select è sfortunatamente piuttosto primitiva. Per esempio

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

È necessario utilizzare FD_ZERO, non FD_CLR per azzerare il set di risultati. – JeremyP

+0

Il mio male. Dovrebbe essere FD_ZERO, ovviamente. – bjg

0

fd_set è in realtà una bitmap. Forse puoi combinarli usando binario o.

typedef long int __fd_mask; 

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

Potrebbe essere necessario prestare particolare attenzione alla dimensione di fd_set: può anche essere allocata dinamicamente. Accedere ai byte dopo '(nfds + 7)/8' (dove' nfds' è il primo argomento select()) dovrebbe essere evitato. A meno che, naturalmente, non si possa confermare che le librerie stanno effettivamente utilizzando il corretto fd_set da sys/select.h. – Dummy00001

+1

Ad eccezione delle applicazioni che richiedono molte prestazioni, raccomanderei fortemente questo approccio. Guardare all'interno di strutture dati opache e scrivere codice che dipende dalla loro implementazione è una pessima pratica. –

1

senza fare affidamento sul implementazione interna del fd_set, il meglio che puoi fare è una semplice coppia di anelli

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; 
} 

Dove i parametri n sono il più grande numero descrittore potenzialmente nel corrispondente set, più uno (vale a dire l'argomento nfds che si passerebbe a select se si stesse usando solo quel set).

Modifica: Modificato per prendere i puntatori come argomenti, poiché è quello che hai detto che stai ricevendo dalle tue librerie.

+0

Restituire un fd_set sembra una pessima idea. È passato un po 'di tempo da quando ho letto POSIX, ma potrebbe persino consentire a fd_set di essere un tipo di array, nel qual caso questo codice fallirebbe. Meglio passare un puntatore a un fd_set e lasciare che la funzione scriva in quel ... –

1

Scorrere un set e controllare ogni fd fino a FD_SETSIZE con FD_ISSET() e se è impostata, impostare il fd in altra serie utilizzando FD_SET()

0

Non credo che si può fare portabile , se si riscrive il codice per utilizzare poll() è possibile unire i set che utilizza dal momento che sono solo array di portabile struct s

5
codice

C che non dipendono dalla realizzazione di 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

in FDSet_Merge() si esegue solo il loop fino al minimo di NFds1 e NFds2. È necessario eseguire il ciclo fino al massimo dei due – JeremyP

+0

@JeremyP - grazie, corretto. –

+0

Non dovrebbe essere "const fd_set * In", e così via? – unwind