Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use the poll C function to watch named pipes in Linux?

Tags:

c

linux

I am trying to write a program where i am supposed to watch the ends of some named pipes using poll function. I have a for loop to check every pipe whenever poll returns >0 and i know that when a pipe gets closed from the procedure at the other end, i will get POLLHUP or POLLIN | POLLHUP in the revents field of the pollfd struct.

My question is: when one pipe does indeed get closed and returns POLLHUP to me, what will happen on the next loop? Is it going to return POLLHUP again and again in the next and any subsequent loop or is poll function going to ignore it after the first POLLHUP?

like image 799
nikos Avatar asked May 02 '11 13:05

nikos


People also ask

What does poll () do in C?

The poll() function identifies those file descriptors on which an application can read or write data, or on which an error event has occurred. A pointer to an array of pollfd structures, pollmsg structures, or to a pollist structure.

How does poll work Linux?

poll and select have essentially the same functionality: both allow a process to determine whether it can read from or write to one or more open files without blocking. They are thus often used in applications that must use multiple input or output streams without blocking on any one of them.

Does Linux support named pipes?

In Linux, we have two types of pipes: pipes (also known as anonymous or unnamed pipes) and FIFO's (also known as named pipes).


2 Answers

Minimal example

Source below. Usage:

sudo mknod poll0.tmp p
sudo mknod poll1.tmp p
sudo chmod 666 poll*.tmp
./poll.out

On another shell:

printf a > poll0.tmp
printf b > poll1.tmp

Output:

loop
POLLIN i=0 n=1 buf=a
loop
POLLHUP i=0
loop
POLLIN i=1 n=1 buf=b
POLLHUP i=1
loop

So notice how poll waits for the reads without looping.

Cooler example:

(while true; do date; sleep 1; done) > poll0.tmp &
(while true; do date; sleep 2; done) > poll1.tmp &

0 gets written every one second, and 1 every two seconds, which shows how poll() is dealing with both inputs concurrently, without stalling each other.

Source:

poll.c

#define _XOPEN_SOURCE 700
#include <fcntl.h> /* creat, O_CREAT */
#include <poll.h> /* poll */
#include <stdio.h> /* printf, puts, snprintf */
#include <stdlib.h> /* EXIT_FAILURE, EXIT_SUCCESS */
#include <unistd.h> /* read */

int main(void) {
    enum { N = 2 };
    char buf[1024], path[1024];
    int fd, i, n;
    short revents;
    struct pollfd pfds[N];

    for (i = 0; i < N; ++i) {
        snprintf(path, sizeof(path), "poll%d.tmp", i);
        /* O_NONBLOCK is required or else the open blocks
         * until the other side of the pipe opens. */
        fd = open(path, O_RDONLY | O_NONBLOCK);
        if (fd == -1) {
            perror("open");
            exit(EXIT_FAILURE);
        }
        pfds[i].fd = fd;
        /* Only events in this mask will be listened to.
         * However, there are also some events that are unmaskable,
         * notably POLLHUP when pipe closes! */
        pfds[i].events = POLLIN;
    }
    while (1) {
        puts("loop");
        i = poll(pfds, N, -1);
        if (i == -1) {
            perror("poll");
            exit(EXIT_FAILURE);
        }
        for (i = 0; i < N; ++i) {
            revents = pfds[i].revents;
            if (revents & POLLIN) {
                n = read(pfds[i].fd, buf, sizeof(buf));
                printf("POLLIN i=%d n=%d buf=%.*s\n", i, n, n, buf);
            }
            if (revents & POLLHUP) {
                printf("POLLHUP i=%d\n", i);

                /* This happens when the other side closed.
                 * This event is only cleared when we close the reader. */

                /* poll won't set POLLHUP anymore once all fds are closed.
                 * Any futher polls on this will give the POLLNVAL event instead. */
                close(pfds[i].fd);

                /* negative fds are ignored. So if we negate an FD,
                 * we can both turn if off for a while, and turn it on
                 * later on by re-nagating it. */
                pfds[i].fd *= -1;
            }
        }
    }
}

Compile with:

gcc -o poll.out -std=c99 poll.c

Tested in Ubuntu 14.04.

GitHub upstream.

To answer the original question:

when one pipe does indeed get closed and returns POLLHUP to me, what will happen on the next loop? Is it going to return POLLHUP again and again in the next and any subsequent loop or is poll function going to ignore it after the first POLLHUP?

Remove the lines:

close(pfds[i].fd);
pfds[i].fd *= -1;

and you will see that it loops forever over POLLHUP.

Remove just:

close(pfds[i].fd);

and you get POLLNVAL instead, as it tries to use a closed fd: How to handle the Linux socket revents POLLERR, POLLHUP and POLLNVAL?


It will continue to set POLLHUP in revents.
Also, see http://linux.die.net/man/3/poll for reference.

like image 38
Josh Avatar answered Oct 04 '22 04:10

Josh