5
votes

I was reading about poll in C programming and built an application given on the poll(2) man page.

Here is the example:

#include<stdio.h>
#include <stropts.h>
#include <poll.h>
#include <fcntl.h>

int main() {

    struct pollfd fds[2];
    int timeout_msecs = -1;
    int ret;
    int i;

    /* Open STREAMS device. */
    fds[0].fd = open("/home/jeshwanth/mywork/poll/dev0", O_RDONLY);
    fds[1].fd = open("/home/jeshwanth/mywork/poll/dev1", O_RDONLY);
    fds[0].events = POLLOUT | POLLWRBAND;
    fds[1].events = POLLOUT | POLLWRBAND;

    while (1) {
        ret = poll(fds, 2, timeout_msecs);

        if (ret > 0) {
            /* An event on one of the fds has occurred. */
            for (i = 0; i < 2; i++) {
                if (fds[i].revents != 0) {
                    /* Priority data may be written on device number i. */
                    printf(
                            "Priority Data may be written on device number %d POLLWRBAND\n",
                            i);

                }
                if (fds[i].revents = !0) {
                    /* Data may be written on device number i. */
                    printf("Data may be written on device number %d POLLOUT\n",
                            i);
                }
                if (fds[i].revents = !0) {
                    /* A hangup has occurred on device number i. */
                    printf("A hangup has occurred on device number %d\n", i);

                }
            }
        }
    }
    return 0;
}

Note: dev0 and dev1 are normal files. When I run the program, if no event occurred in dev0 and dev1, the message is displayed. But I was expecting when some write into the file happens, only then should it display the message. Am I wrong?

3
None of those printf should be executed as each of those enclosing if branches have conditions of 0.obataku
@oldrinb thanks, I was testing it before posting here so by mistake I put =, now it is !=. Forget about those printf's, how ret > 0 ? if there is no change in files ?duslabo
Your code is still wrong, but now it will print the latter 2 of the messages, as their enclosing if branches all have conditions of !0.obataku
You should review how poll() works.j.w.r
poll is always suceessful for I/O on disk files (or thru the filesystem).Basile Starynkevitch

3 Answers

3
votes

Polling it for output readiness doesn't mean you will get notified when some output occurs: it means that you'll get notified when there is output buffer space available so you can output (but you should still check the return value of your output function. The buffer state may have changed between polling and outputting; always check return values).

2
votes

Minimal FIFO named pipe example

You won't be able to see anything interesting with regular files, since those always give POLLIN immediately: How can select() wait on regular file descriptors (non-sockets)?

The simplest way to play around with poll is to use named pipes as shown below. This should prepare you for their major application: sockets and device files.

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:

#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.

The lines:

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

are required or else you get POLLHUP forever, see also: How to use the poll C function to watch named pipes in Linux?

For even more fun, create a Linux kernel module what implements the poll fops: How to add poll function to the kernel module code?

0
votes

I'll give you a hint on how to correct it. revents is interpreted as several bit flags.

/* check for priority write readiness */
if (fds[i].revents & POLLWRBAND) {
  printf("Priority Data may be written on device number %d POLLWRBAND\n", i);
}

/* check for write readiness */
if (fds[i].revents & POLLOUT) {
  printf("Data may be written on device number %d POLLOUT\n", i);
}

/* check for hang-up */
if (fds[i].revents & POLLHUP) {
  printf("A hangup has occurred on device number %d\n", i);
}