user4013391
user4013391

Reputation: 117

How to constantly read FIFO in C

I want to read a fifo pipe constantly in C. My code example works, but since i use while(1) one of my CPU cores is at 100% all the time.

So my question: Is there a smoother way to read the fifo, without killing the CPU?

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>

bool suffix (char* base, char* str) {
    int blen = strlen(base);
    int slen = strlen(str);
    return (blen >= slen) && (0 == strcmp(base + blen - slen, str));
}

bool prefix(const char *pre, const char *str)
{
    return strncmp(pre, str, strlen(pre)) == 0;
}


void chomp(char *s) {
    while(*s && *s != '\n' && *s != '\r') s++;
    *s = 0;
}

int main(int argc, char *argv[])
{
    int fifo, c=0;
    char buf[200];

    char fifo_name[] = "/var/log/proftpd_log_all.fifo";
    fifo = open(fifo_name, O_RDONLY|O_NONBLOCK);

    while (1)
    {
        if (read(fifo, &buf, sizeof(char)*200) > 0)
        {
            if (prefix("[STOR]",buf)) {
                //printf("%s \n", buf);
                if (strstr(buf, ".jpf") != NULL) {
                    //...
                }       
            }
        }
    }
    printf("exit");
    close(fifo);
    return 0;
}

Upvotes: 2

Views: 2295

Answers (2)

Ternvein
Ternvein

Reputation: 305

In this case I'd use select() for polling. You can do it like this:

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/select.h>

int main()
{
    fd_set readCheck;
    fd_set errCheck;
    char buffer[64];
    struct timeval timeout;
    int rv;

    int fd = open("./fifo.test", O_RDONLY | O_RSYNC);

    FD_ZERO(&readCheck);
    FD_ZERO(&errCheck);

    while (1) {
        FD_SET(fd, &readCheck);
        FD_SET(fd, &errCheck);

        timeout.tv_sec = 1;
        timeout.tv_usec = 0;

        rv = select(fd, &readCheck, NULL, &errCheck, &timeout);
        if (rv < 0) {
            printf("Select failed\r\n");
            break;
        }

        if (FD_ISSET(fd, &errCheck)) {
            printf("FD error\r\n");
            continue;
        }

        if (FD_ISSET(fd, &readCheck)) {
            memset(buffer, 0, sizeof(buffer));
            rv = read(fd, buffer, sizeof(buffer));
            if (rv < 0) {
                printf("Read failed\r\n");
                break;
            }
            buffer[63] = '\0';
            printf(buffer);
        }
    }
    close(fd);

    return 0;
}

It should work for FIFO files, created with mkfifo(). You can also set infinite timeout with NULL instead of timeout in function call.

This will reduce your CPU utilization greatly and will allow you to read only real data. Take note that if you use a simple file instead of a FIFO the select() function will wait forever for it to update, because simple files are always ready to read.

You can also read the following mans for more info: select, pselect, poll

Upvotes: 1

Sudesh Gutta
Sudesh Gutta

Reputation: 47

You have 3 methods for handling it:-

  1. You can use Blocking mode while reading from FIFO.

  2. Simplest method is to use sleep() within the while loop to suspend the thread for sometime , this would reduce cpu load momentarily.

  3. You can use signals & interrupts,writer can send a signal whenever it writes something to FIFO.The reader can be suspended until a signal is received from the writer of FIFO & performs the read operation until end of FIFO and suspends again.

Upvotes: 2

Related Questions