LinuxQuestions.org
Review your favorite Linux distribution.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Blogs > rtmistler
User Name
Password

Notices


Rate this Entry

Select(2) and why I use it

Posted 05-24-2013 at 01:55 PM by rtmistler
Updated 06-03-2013 at 07:57 AM by rtmistler

What does select() for me? I mainly use it as a non-blocking call which rapidly checks file descriptors for new information. Based on this, I can continue to perform other computing activities which are in my processes background, but also be ready to grab data from those one or more descriptors as soon as something is available.

The following code has been simplified to cover the main points surround how I use select().

Code:
__attribute__((noreturn))void run_sensor_process(int rx_fd, int tx_fd)
{
    /* Open the serial connection to some sort of sensor device */
    int sensor_fd = open_sensor();

    while(1) {
        process_descriptors(rx_fd, tx_fd, sensor_fd);

        /* Do other non-related processing */
        /* Like check and maintain the sensor connection and the UI connection */
    }
}
Code:
void process_descriptors(int rx_fd, int tx_fd, int sensor_fd)
{
    int ret_val;
    struct timeval ts = { 0, 0 };
    fd_set fds;

    /* Initialize the file descriptor to make it empty */
    FD_ZERO(&fds);

    /* If either descriptor is connected, add them to this FD descriptor set */
    if(rx_fd != -1) {
        FD_SET(rx_fd, &fds);
    }
    if(sensor_fd != -1) {
        FD_SET(sensor_fd, &fds);
    }

    /* If both not connected, then exit now */
    if((rx_fd == -1) && (sensor_fd == -1)) {
        return;
    }

    /*
     * Select() will check all descriptors in the FD set, to invoke it, use
     * the highest numbered descriptor in your set "plus 1".  In this case
     * we're using only the readfds for select(), therefore the two NULL
     * values are the writefds, and the exceptfds.  In this case we're
     * only checking for data being received by this process.
     *
     * NOTE: The timespec structure has been set to zero, which means there will
     * be no blocking, or delay in the return from select().  However, upon
     * exit from select() we can check the FD set to see if any descriptors have
     * been activated, and we also check the return from select().
     */
    if(sensor_fd > rx_fd) {
        ret_val = select((sensor_fd + 1), &fds, NULL, NULL, &ts);
    }
    else {
        ret_val = select((rx_fd + 1), &fds, NULL, NULL, &ts);
    }

    /* If select() returned a non-negative number data may have been received */
    if(ret_val >= 1) {
        if(rx_fd != -1) {
            if(FD_ISSET(rx_fd, &fds)) {
                /* We had data from the UI, act upon that data */
                parse_ui_command(rx_fd, sensor_fd);
            }
        }
        if(sensor_fd != -1) {
            if(FD_ISSET(sensor_fd, &fds)) {
                /* We had data from the sensor, read it, parse it, and send it to the GUI */
                read_sensor(sensor_fd, tx_fd);
            }
        }
    }
    /* Otherwise, select() may have returned with no data, but not necessarily a bad thing */
    else {
        if((errno != 2) && (errno != 11)) {
            debug_log("Error return from select() %d(%s)\n", errno, strerror(errno));
        }
        return;
    }

    return;
}
Summary of what this does:

- Firstly, there are no guarantees. The serial sensor device may go away and have to be re-established. That's part of the not shown code in the main loop. Same thing for the connection to the GUI, the GUI process may fail and get restarted, therefore we're checking the value of these interface descriptors where the convention is < 0, the descriptor is not valid. Technically == 0 is also not valid, but I only check so many cases and the descriptor management code only legally sets for -1 or positive values.
- The main loop calls process_descriptors() which invokes select().
- Select() does not block, it returns immediately. But it gives an indication as to whether or not there is data on either file descriptor. Allowing the code following a successful return from select() to determine which file descriptor(s) have new data.

- This run-forever process doesn't block or have timeouts, therefore as fast as it can process serial data, it will.
- The process doesn't cause starvation of the CPU, the select() call allows the OS to let other processes run, and the code here is written to not do much if there is no new data.

The alternate to this would be to have a forever loop, with a nanosleep() call, and then "tweak" the time span for the nanosleep() to get it to be efficient for my application. The multitude of problems here I've found are:
- This is ONE receive case, I actually have several sensor processes.
- The data from the GUI and the sensor comes in bursts, not paced continuously on the input descriptors. Therefore having evenly spaced calls to receive(2) doesn't necessarily serve the needs, where it's better and more efficient to only deal with data, when it is available.

How do you use select()?
Views 2183 Comments 0
« Prev     Main     Next »
Total Comments 0

Comments

 

  



All times are GMT -5. The time now is 05:14 AM.

Main Menu
Advertisement
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration