Search code examples
csocketsnetwork-programmingposix-select

How does select() modify its input sets?


I know select modifies the input sets upon return. But how? How will a fd that's in an input set become not in the input set? fd_isset(fd) will return true, it seems to me, as long as fd was in the input set at the beginning. So how exactly does select() modify its input sets?

This is the fd_set structure:

typedef struct fd_set {
  u_int  fd_count;
  SOCKET fd_array[FD_SETSIZE];
} fd_set;

So which fields will be updated by select()? We still want to track all of the fd's so I don't see how it makes sense for select() to change fd_count or fd_array? So, what DOES select() change?

From man page:

"On exit, the sets are modified in place to indicate which file descriptors actually changed status."

But details about WHAT aspect of the sets are modified are glaringly missing.

EDIT: In response to the answers: So is the following the correct way of using this?

while (not error and not socket closes){
    FD_ZERO(&readSet);
    FD_ZERO(&writeSet);
    FD_SET(fd, &readSet);
    FD_SET(socket_num, &readSet);
    FD_SET(fd, &writeSet);
    FD_SET(socket_num, &writeSet);
    select(nfds, &readSet, writeSet, NULL, NULL);
    if(FD_ISSET(fd, &readSet)){
      read(fd, buf, sizeof(buf));
      write(socket_num, buf, sizeof(buf));
      /*memset(buf, 0, sizeof(buf));*/
    }
    if(FD_ISSET(socket_num, &readSet)){
      read(socket_num, buf, sizeof(buf));
      write(fd, buf, sizeof(buf));
    }
}

Solution

  • On input, the fd_set tells select() which FDs it should be checking. select makes a copy of this list for its internal use. When it returns, it modifies the input structure to contain the returned information. It can modify both fd_count and fd_array.

    This is why loops that use select() typically save a copy of the fd_set before the loop, and copy this back to the variable that's used in the select() call each time through the loop, e.g.

    FD_SET(sockfd, &fd_set_init);
    while(true) {
        FD_COPY(&fd_set_init, &fd_set);
        n = select(nfds, &fd_set, NULL, NULL, NULL);
        if (n > 0) {
            // Use fd_set here
        }
    }