select (Unix)

select is a system call and application programming interface (API) in Unix-like and POSIX-compliant operating systems for examining the status of file descriptors of open input/output channels. The select system call is similar to the poll facility introduced in UNIX System V and later operating systems. However, with the c10k problem, both select and poll have been superseded by the likes of kqueue, epoll, /dev/poll and I/O completion ports.

One common use of select outside of its stated use of waiting on filehandles is to implement a portable sub-second sleep. This can be achieved by passing NULL for all three fd_set arguments, and the duration of the desired sleep as the timeout argument.

In the C programming language, the select system call is declared in the header file sys/select.h or unistd.h, and has the following syntax:

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);

fd_set type arguments may be manipulated with four utility macros:FD_SET(), FD_CLR(), FD_ZERO(), andFD_ISSET().

Select returns the total number of bits set inreadfds, writefds anderrorfds, or zero if the timeout expired, and -1 on error.

The sets of file descriptor used in select are finite in size, depending on the operating system. The newer system call poll provides a more flexible solution.

Example

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#include <sys/select.h>
#include <fcntl.h>
#include <unistd.h>
#include <err.h>
#include <errno.h>

#define PORT "9421"

/* function prototypes */
void die(const char*);

int main(int argc, char **argv)
{
    int sockfd, new, maxfd, on = 1, nready, i;

    struct addrinfo *res0, *res, hints;

    char buffer[BUFSIZ];

    fd_set master, readfds;

    int error;

    ssize_t nbytes;

    (void)memset(&hints, '\0', sizeof(struct addrinfo));

    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;

    if (0 != (error = getaddrinfo(NULL, PORT, &hints, &res0)))
        errx(EXIT_FAILURE, "%s", gai_strerror(error));

    for (res = res0; res; res = res->ai_next)
    {
        if (-1 == (sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)))
        {
            perror("socket()");
            continue;
        }

        if (-1 == (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(int))))
        {
            perror("setsockopt()");
            continue;
        }

        if (-1 == (bind(sockfd, res->ai_addr, res->ai_addrlen)))
        {
            perror("bind()");
            continue;
        }

        break;

    }

    if (-1 == sockfd)
        exit(EXIT_FAILURE);

    freeaddrinfo(res0);

    if (-1 == (listen(sockfd, 32)))
        die("listen()");

    if (-1 == (fcntl(sockfd, F_SETFD, O_NONBLOCK)))
        die("fcntl()");

    FD_ZERO(&master);
    FD_ZERO(&readfds);

    FD_SET(sockfd, &master);

    maxfd = sockfd;

    while (1)
    {
        memcpy(&readfds, &master, sizeof(master));

        (void)printf("running select()\n");

        if (-1 == (nready = select(maxfd+1, &readfds, NULL, NULL, NULL)))
            die("select()");

        (void)printf("Number of ready descriptor: %d\n", nready);

        for (i=0; i<=maxfd && nready>0; i++)
        {
            if (FD_ISSET(i, &readfds))
            {
                nready--;

                if (i == sockfd)
                {
                    (void)printf("Trying to accept() new connection(s)\n");

                    if (-1 == (new = accept(sockfd, NULL, NULL)))
                    {
                        if (EWOULDBLOCK != errno)
                            die("accept()");

                        break;
                    }

                    else
                    {

                        if (-1 == (fcntl(new, F_SETFD, O_NONBLOCK)))
                            die("fcntl()");

                        FD_SET(new, &master);

                        if (maxfd < new)
                            maxfd = new;
                    }
                }

                else
                {
                    (void)printf("recv() data from one of descriptors(s)\n");

                    nbytes = recv(i, buffer, sizeof(buffer), 0);
                    if (nbytes <= 0)
                    {
                        if (EWOULDBLOCK != errno)
                            die("recv()");

                        break;
                    }

                    buffer[nbytes] = '\0';
                    printf("%s", buffer);

                    (void)printf("%zi bytes received.\n", nbytes);

                    close(i);
                    FD_CLR(i, &master);

                }
            }

        }

    }
    return 0;
}

void die(const char *msg)
{
    perror(msg);
    exit(EXIT_FAILURE);
}

See also

References

Uses material from the Wikipedia article select (Unix), released under the CC BY-SA 4.0 license.