Unix Network Programming Manual
Select
The previous example demonstrated how to perform asynchronous input
by polling the input sources. In the
broadcast server, the sources were the UDP port used by clients to
broadcast ping servers and the connection port used for connecting clients.
Each client then had to manage the I/O for each client. If the number of
sources of input gets large, or the relationships get more complicated,
this is difficult to do. For example, if you have to access different
input sources under different conditions, rather than in a simple loop.
In such situations, Unix provides another capability called the select
function. This functions allows a programmer to determine where the
I/O signals are originating from before polling each source, and therefore,
simplifying the handling of the I/O significantly.
The syntax of the select function is:
int count = select
(
int nfds,
int *readfds,
int *writefds,
int *exceptfds,
struct timeval *timeout
)
The function checks to determine which devices currently have events pending
and therefore, may require I/O operations. The arguments are:
- nfds is the number of devices that should be checked from 0 to nfds-1.
- readfds is the set of devices with reads pending
- writefds is the set of devices with writes pending
- exceptfds is the set of devices with exceptions pending
- timeout is the length of time that select should wait to return
For example, suppose that you had opened a socket as file descriptor 3,
a read-only file as file descriptor 4 and a printer as file descriptor
5. You call the select function to determine if there is a read or write
waiting on file descriptor 3, a read completion on 4 and a write completion
on 5. The call could be done like this:
count = select (6, 0x00000018, 0x00000028, NULL, NULL);
The count returned is the number file descriptor bits set (having some
sort of I/O pending). 6 indicates that file descriptors 0 - 5 can have I/O
pending. 0x00000018 indicates that file descriptors 3 and 4 should be
checked for reads pending; 0x00000028 requests that file descriptors 3 and
5 should be checked for writes pending.
The exceptfds set indicates those file descriptors that have a read, a write
or some other signal condition pending. The NULL value for exceptfds
indicates that there is no interest in checking for this. exceptfds is
often used to find all devices with I/O pending regardless of type. Then,
another call is used to determine what needs to be done.
The timeout value of NULL indicates that no time is to be spent waiting
for events to accumulate. In some cases, the timeout can be used in a
manner similar to a pause. select will wait for some length of time and
then return with the current I/O state, which is better than sitting in
a busy wait loop.
When the function returns, the readsfds, writefds and exceptfds arguments
are modified to indicate which of the devices has experienced an I/O
event. The return from this function might be:
count = 2
readfds = 0x00000010
writefds = 0x00000008
indicating that a read is pending on the open file, and that the socket
has completed a previously started write process.
It is important to recognize what is meant by read and write events. If
a write operation is started on a file or socket, the completion of the
write is an event. If data arrives at a socket, that is a read event, in
that I/O is possible. If a read is started on a file, the completion of
the read is an event, because another read is now possible. A program
can place a call to the select function in the signal handler for SIGIO
to determine which devices currently need service, where that service is
to read data or to start another write. Most uses of select are in
I/O signal handlers, but there are other uses as well.
An example of using select is shown below. The FD_ macros are described
in the select man page. The next page provides an example of using select
to handle asynchronous I/O.
int readfds, writefds;
int sock1, sock2;
FD_ZERO (&readfds);
FD_ZERO (&writefds);
FD_SET (sock1, &readfds);
FD_SET (sock2, &readfds);
FD_SET (sock1, &writefds);
FD_SET (sock2, &writefds);
select (16, &readfds, &writefds, NULL, NULL);
if (FD_ISSET (sock1, &readfds)
sock1 and sock2 are sockets that are using asynchronous I/O
so the arguments are configured so that select will indicate when
either input or output events have occurred. After return, the bits
are checked to see what needs to be done.