There is a concept of synchronous polling for multiple device files in linux and I'm trying to understand how it works.
in linux 2.6.23 source drivers/char/random.c, I see following code
static DECLARE_WAIT_QUEUE_HEAD(random_read_wait);
static DECLARE_WAIT_QUEUE_HEAD(random_write_wait);
static unsigned int
random_poll(struct file *file, poll_table * wait)
{
unsigned int mask;
poll_wait(file, &random_read_wait, wait);
poll_wait(file, &random_write_wait, wait);
mask = 0;
if (input_pool.entropy_count >= random_read_wakeup_thresh)
mask |= POLLIN | POLLRDNORM;
if (input_pool.entropy_count < random_write_wakeup_thresh)
mask |= POLLOUT | POLLWRNORM;
return mask;
}
the poll_table is defined as below in include/linux/poll.h
typedef void (*poll_queue_proc)(struct file *, wait_queue_head_t *, struct poll_table_struct *);
typedef struct poll_table_struct {
poll_queue_proc qproc;
} poll_table;
I saw in a book (Ch.5, Essential Linux Device Drivers, Venkateswaran) that "The poll_table is a table of wait queues owned by device drivers that are being polled for data." but the source says it is just a function pointer. and I can't find what this function qproc is doing. Below is the function poll_wait defined in include/linux/poll.h.
static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
{
if (p && wait_address)
p->qproc(filp, wait_address, p);
}
and in the book it says(about an example char driver for a mouse), "mouse_poll() uses the library function, poll_wait(), to add a wait queue (mouse_wait) to the kernel poll_table and go to sleep." so poll_wait can sleep, but in the random_poll() function above, we see tow consecutive poll_wait functions. so does the random_poll polls for read and write availability sequentially and sends the mask to the application? I would appreciate if someone can show me the example of poll_queue_proc function. I couldn't find it in the linux driver source(should it appear only in application?).
drivers/char/random.c:random_poll()
is called when userspace calls
select()
(or poll()
or epoll_wait()
for that matter) with a file
descriptor referring to /dev/random
.
These system calls are at the basis of event multiplexing. In the
following program, userspace opens a number of input sources (say
/dev/random
and /dev/ttyS4
) and calls select()
on both of
them to block until any of them has input data to be read. (There
are other event sources than input, input is just the simplest.)
#include <sys/select.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#define _SYSE(ret, msg) do { \
if (ret == -1) { \
perror(msg); \
exit(EXIT_FAILURE); \
} \
} while (0)
static int /*bool: EOF detected*/ consume_fd(int fd, const char* msg)
{
char tmp[64];
ssize_t nread;
nread = read(fd, tmp, sizeof(tmp));
_SYSE(nread, "read");
if (nread == 0 /*EOF*/)
return 1;
printf("%s: consumed %ld bytes\n", msg, nread);
return 0;
}
int main(void)
{
int random_fd, tty_fd, nfds = 0;
random_fd = open("/dev/random", O_RDONLY);
_SYSE(random_fd, "open random");
if (random_fd > nfds)
nfds = random_fd+1;
tty_fd = open("/dev/ttyS4", O_RDONLY);
_SYSE(tty_fd, "open tty");
if (tty_fd > nfds)
nfds = tty_fd+1;
while (1) {
fd_set in_fds;
int ret;
FD_ZERO(&in_fds);
FD_SET(random_fd, &in_fds);
FD_SET(tty_fd, &in_fds);
ret = select(nfds, &in_fds, NULL, NULL, NULL);
_SYSE(ret, "select");
if (FD_ISSET(random_fd, &in_fds)) {
int eof_detected = consume_fd(random_fd, "random");
if (eof_detected)
break;
}
if (FD_ISSET(tty_fd, &in_fds)) {
int eof_detected = consume_fd(tty_fd, "tty");
if (eof_detected)
break;
}
}
return 0;
}
Output will appear once either random numbers are available, or the
serial line has data. (Note that nowadays /dev/random
does not
block, but rather generates pseudo random numbers, so output is really
fast.)
It is when the select()
call enters the kernel that random_poll()
is called, and another, comparable, function somewhere in the TTY
layer - simply because select()
passes those file descriptors as
parameters. Those functions are supposed to simply enqueue the caller
into a poll_table
that is maintained out of your reach (it
represents the calling task for that purpose).
In a second stage the implementation of select()
then suspends the
caller until any of the events become true. (See fs/select.c
.)