unix select() call: how to combine fd_sets?
I am writing an application in C for linux, which uses 2 separate third-party libraries. Both libraries are asynchronous and use select(). They also provide an API which returns the file descriptors they wait on. My intention is to pass these into my own select() and then return control back to whichever library when their own fd values are set.
I think I've got most of it written, but I have trouble at the point where the select() parameters are concerned: Both libraries do not give individual file descriptors, but pointers to their read and write fd_sets. I need to combine the returned fd_sets from these libraries into one fd_set for read, one fd_set for write, etc.
Any suggestions on how I can combine 2 fd_sets in开发者_开发百科to one resulting fd_set?
Addendum Sorry! I should have been clearer.. these libraries only return the fd_sets... I don't know the number of FDs in each set so that I can do a for loop and set each FD individually.. is there a simple way of determining this given just an fd_set?
C code which doesn't depend on the implementation of 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);
}
You can set up your own fd_set
by looping over all possible file descriptors that each library could possibly have open and in each of their returned sets and set your's accordingly. It's a bit brute-force but the select interface is unfortunately quite primitive. For example
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);
}
}
Iterate through one set and check each fd up to FD_SETSIZE with FD_ISSET() and if it is set, set the fd in the other set using FD_SET()
Without relying on the internal implementation of fd_set
, the best you can do is a simple pair of loops
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;
}
Where the n
parameters are the largest descriptor number potentially in the corresponding set, plus one (i.e. the nfds
argument that you would pass to select
if you were using just that set).
Edit: Changed to take pointers as arguments, since that's what you said you are getting from your libraries.
The fd_set is actually a bitmap. Maybe you can combine them using binary or.
typedef long int __fd_mask;
typedef struct
{
__fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS];
} fd_set;
I don't think you can do that portably, however if you rewrite your code to use poll()
you can merge the sets it uses since they are just arrays of portably struct
s
精彩评论