select Subroutine

Purpose

Checks the I/O status of multiple file descriptors and message queues.

Library

Standard C Library (libc.a)

Syntax

#include <sys/time.h>
#include <sys/select.h>
#include <sys/types.h>

int select (Nfdsmsgs, ReadList, WriteList, ExceptList, TimeOut)
int  Nfdsmsgs;
struct sellist * ReadList, *WriteList, *ExceptList;
struct timeval * TimeOut;

Description

The select subroutine checks the specified file descriptors and message queues to see if they are ready for reading (receiving) or writing (sending), or if they have an exceptional condition pending.

When selecting on an unconnected stream socket, select returns when the connection is made. If selecting on a connected stream socket, then the ready message indicates that data can be sent or received. Files descriptors of regular files always select true for read, write, and exception conditions. For more information on sockets, refer to "Understanding Socket Connections" and the related "Checking for Pending Connections Example Program" dealing with pending connections in AIX® Version 6.1 Communications Programming Concepts.

The select subroutine is also supported for compatibility with previous releases of this operating system and with BSD systems.

On shared memory descriptors, the select subroutine returns true.

Note: If selecting on a non-blocking socket for both read and write events and if the destination host is unreachable, select could show a different behavior due to timing constraints. Refer to the Examples section of this document for further information..

Parameters

Item Description
Nfdsmsgs Specifies the number of file descriptors and the number of message queues to check. The low-order 16 bits give the length of a bit mask that specifies which file descriptors to check; the high-order 16 bits give the size of an array that contains message queue identifiers. If either half of the Nfdsmsgs parameter is equal to a value of 0, the corresponding bit mask or array is assumed not to be present.
TimeOut Specifies either a null pointer or a pointer to a timeval structure that specifies the maximum length of time to wait for at least one of the selection criteria to be met. The timeval structure is defined in the /usr/include/sys/time.h file and it contains the following members:
struct timeval {
   int tv_sec;        /* seconds       */
   int tv_usec;       /* microseconds  */
    };

The number of microseconds specified in TimeOut.tv_usec, a value from 0 to 999999, is set to one millisecond if the process does not have root user authority and the value is less than one millisecond.

If the TimeOut parameter is a null pointer, the select subroutine waits indefinitely, until at least one of the selection criteria is met. If the TimeOut parameter points to a timeval structure that contains zeros, the file and message queue status is polled, and the select subroutine returns immediately.

ReadList, WriteList, ExceptList Specify what to check for reading, writing, and exceptions, respectively. Together, they specify the selection criteria. Each of these parameters points to a sellist structure, which can specify both file descriptors and message queues. Your program must define the sellist structure in the following form:
struct sellist
{
ulong fdsmask[F];        /* file descriptor bit mask  */
int msgids[M];         /* message queue identifiers */
};

The fdsmask array is treated as a bit string in which each bit corresponds to a file descriptor. File descriptor n is represented by the bit(1 << (n mod bits)) in the array element fdsmask[n / BITS(int)]. (The BITS macro is defined in the values.h file.) Each bit that is set to 1 indicates that the status of the corresponding file descriptor is to be checked.

Note: The low-order 16 bits of the Nfdsmsgs parameter specify the number of bits (not elements) in the fdsmask array that make up the file descriptor mask. If only part of the last int is included in the mask, the appropriate number of low-order bits are used, and the remaining high-order bits are ignored. If you set the low-order 16 bits of the Nfdsmsgs parameter to 0, you must not define an fdsmask array in the sellist structure.

Each int of the msgids array specifies a message queue identifier whose status is to be checked. Elements with a value of -1 are ignored. The high-order 16 bits of the Nfdsmsgs parameter specify the number of elements in the msgids array. If you set the high-order 16 bits of the Nfdsmsgs parameter to 0, you must not define a msgids array in the sellist structure.

Note: The arrays specified by the ReadList, WriteList, and ExceptList parameters are the same size because each of these parameters points to the same sellist structure type. However, you need not specify the same number of file descriptors or message queues in each. Set the file descriptor bits that are not of interest to 0, and set the extra elements of the msgids array to -1.

You can use the SELLIST macro defined in the sys/select.h file to define the sellist structure. The format of this macro is:

SELLIST(f, m) declarator . . . ;

where f specifies the size of the fdsmask array, m specifies the size of the msgids array, and each declarator is the name of a variable to be declared as having this type.

Return Values

Upon successful completion, the select subroutine returns a value that indicates the total number of file descriptors and message queues that satisfy the selection criteria. The fdsmask bit masks are modified so that bits set to 1 indicate file descriptors that meet the criteria. The msgids arrays are altered so that message queue identifiers that do not meet the criteria are replaced with a value of -1.

The return value is similar to the Nfdsmsgs parameter in that the low-order 16 bits give the number of file descriptors, and the high-order 16 bits give the number of message queue identifiers. These values indicate the sum total that meet each of the read, write, and exception criteria. Therefore, the same file descriptor or message queue can be counted up to three times. You can use the NFDS and NMSGS macros found in the sys/select.h file to separate out these two values from the return value. For example, if rc contains the value returned from the select subroutine, NFDS(rc) is the number of files selected, and NMSGS(rc) is the number of message queues selected.

If the time limit specified by the TimeOut parameter expires, the select subroutine returns a value of 0.

If a connection-based socket is specified in the Readlist parameter and the connection disconnects, the select subroutine returns successfully, but the recv subroutine on the socket will return a value of 0 to indicate the socket connection has been closed.

For nonbloking connection-based sockets, both successful and unsuccessful connections will cause the select subroutine to return successfully without any error.

When the connection completes successfully the socket becomes writable, and if the connection encounters an error the socket becomes both readable and writable.

When using the select subroutine, you can not check any pending errors on the socket. You need to call the getsockopt subroutine with SOL_SOCKET and SOL_ERROR to check for a pending error.

If the select subroutine is unsuccessful, it returns a value of -1 and sets the global variable errno to indicate the error. In this case, the contents of the structures pointed to by the ReadList, WriteList, and ExceptList parameters are unpredictable.

Error Codes

The select subroutine is unsuccessful if one of the following are true:

Item Description
EBADF An invalid file descriptor or message queue identifier was specified.
EAGAIN Allocation of internal data structures was unsuccessful.
EINTR A signal was caught during the select subroutine and the signal handler was installed with an indication that subroutines are not to be restarted.
EINVAL An invalid value was specified for the TimeOut parameter or the Nfdsmsgs parameter.
EINVAL The STREAM or multiplexer referenced by one of the file descriptors is linked (directly or indirectly) downstream from a multiplexer.
EFAULT The ReadList, WriteList, ExceptList, or TimeOut parameter points to a location outside of the address space of the process.

Examples

The following is an example of the behavior of the select subroutine called on a non-blocking socket, when trying to connect to a host that is unreachable:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <fcntl.h>
#include <sys/time.h>
#include <errno.h>
#include <stdio.h>

int main()
{
    int sockfd, cnt, i = 1;
    struct sockaddr_in  serv_addr;

    bzero((char *)&serv_addr, sizeof (serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = inet_addr("172.16.55.25");
    serv_addr.sin_port = htons(102);

    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        exit(1);
    if (fcntl(sockfd, F_SETFL, FNONBLOCK) < 0)
        exit(1);
    if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof
            (serv_addr)) < 0 && errno != EINPROGRESS)
        exit(1);
    for (cnt=0; cnt<2; cnt++) {
        fd_set  readfds, writefds;

        FD_ZERO(&readfds);
        FD_SET(sockfd, &readfds);
        FD_ZERO(&writefds);
        FD_SET(sockfd, &writefds);

        if (select(sockfd + 1, &readfds, &writefds, NULL,
                NULL) < 0)
            exit(1);
        printf("Iteration %d ==============\n", i);
        printf("FD_ISSET(sockfd, &readfds) == %d\n",
            FD_ISSET(sockfd, &readfds));
        printf("FD_ISSET(sockfd, &writefds) == %d\n",
            FD_ISSET(sockfd, &writefds));
        i++;
    }
    return 0;
}
Here is the output of the above program :
Iteration 1 ==============
FD_ISSET(sockfd, &readfds) == 0
FD_ISSET(sockfd, &writefds) == 1
Iteration 2 ==============
FD_ISSET(sockfd, &readfds) == 1
FD_ISSET(sockfd, &writefds) == 1

In the first iteration, select notifies the write event only. In the second iteration, select notifies both the read and write events.

Notes

FD_SETSIZE is the #define variable that defines how many file descriptors the various FD macros will use. The default value for FD_SETSIZE will vary, depending on the version of AIX. As the number of open files supported has increased, the default value of FD_SETSIZE has increased.

In AIX 4.3.1, the size increased to 32767 open file descriptors (from 2000 in prior releases). In AIX 5.2, the size increased to 65534 open file descriptors. This value can not be set greater than OPEN_MAX, which also varies from one AIX Version to another.

For more information, refer to the /usr/include/sys/time.h file.

The user may override FD_SETSIZE to select a smaller value before including the system header files. This is desirable for performance reasons, because of the overhead in FD_ZERO to zero 65534 bits.

Performance Issues and Recommended Coding Practices

The select subroutine can be a very compute intensive system call, depending on the number of open file descriptors used and the lengths of the bit maps used. Do not follow the examples shown in many text books. Most were written when the number of open files supported was small, and thus the bit maps were short. You should avoid the following (where select is being passed FD_SETSIZE as the number of FDs to process):
select(FD_SETSIZE, ....)

Performance will be poor if the program uses FD_ZERO and the default FD_SETSIZE. FD_ZERO should not be used in any loops or before each select call. However, using it one time to zero the bit string will not cause problems. If you plan to use this simple programming method, you should override FD_SETSIZE to define a smaller number of FDs. For example, if your process will only open two FDs that you will be selecting on, and there will never be more than a few hundred other FDs open in the process, you should lower FD_SETSIZE to approximately 1024.

Do not pass FD_SETSIZE as the first parameter to select. This specifies the maximum number of file descriptors the system should check for. The program should keep track of the highest FD that has been assigned or use the getdtablesize subroutine to determine this value. This saves passing excessively long bit maps in and out of the kernel and reduces the number of FDs that select must check.

Use the poll system call instead of select. The poll system call has the same functionality as select, but it uses a list of FDs instead of a bit map. Thus, if you are only selecting on a single FD, you would only pass one FD to poll. With select, you have to pass a bit map that is as long as the FD number assigned for that FD. If AIX assigned FD 4000, for example, you would have to pass a bit map 4001 bits long.