Что делает функция poll_queue_proc в драйвере char-устройства Linux?

В Linux существует концепция синхронного опроса для нескольких файлов устройств, и я пытаюсь понять, как это работает.
в исходных драйверах linux 2.6.23/char/random.c я вижу следующий код.

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;
}

poll_table определяется, как показано ниже, в 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;

Я видел в книге (глава 5, Essential Linux Device Drivers, Venkateswaran), что «таблица poll_table — это таблица очередей ожидания, принадлежащая драйверам устройств, которые опрашиваются для получения данных». но источник говорит, что это просто указатель на функцию. и я не могу найти, что делает эта функция qproc. Ниже представлена ​​функция poll_wait, определенная в 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);
}

и в книге говорится (о примере драйвера char для мыши): «mouse_poll() использует библиотечную функцию poll_wait(), чтобы добавить очередь ожидания (mouse_wait) в poll_table ядра и перейти в спящий режим». так что poll_wait может спать, но в приведенной выше функции random_poll() мы видим две последовательные функции poll_wait. так что random_poll последовательно опрашивает доступность для чтения и записи и отправляет маску в приложение? Буду признателен, если кто-нибудь покажет мне пример функции poll_queue_proc. Я не смог найти его в источнике драйвера Linux (он должен отображаться только в приложении?).


person Chan Kim    schedule 30.04.2020    source источник


Ответы (1)


drivers/char/random.c:random_poll() вызывается, когда пользовательское пространство вызывает select() (или poll(), или epoll_wait(), если уж на то пошло) с дескриптором файла, ссылающимся на /dev/random.

Эти системные вызовы лежат в основе мультиплексирования событий. В следующей программе пользовательское пространство открывает несколько источников ввода (скажем, /dev/random и /dev/ttyS4) и вызывает select() на обоих для блокировки до тех пор, пока какой-либо из них не получит входные данные для обработки. читать. (Существуют и другие источники событий, помимо ввода, ввод является самым простым.)

#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;
}

Вывод появится, когда будут доступны случайные числа или в последовательной линии появятся данные. (Обратите внимание, что в настоящее время /dev/random не блокирует, а генерирует псевдослучайные числа, поэтому вывод выполняется очень быстро.)

Именно когда вызов select() входит в ядро, вызывается random_poll() и другая аналогичная функция где-то на уровне TTY — просто потому, что select() передает эти файловые дескрипторы в качестве параметров. Предполагается, что эти функции просто ставят вызывающего абонента в очередь в poll_table, который поддерживается вне досягаемости (для этой цели он представляет собой вызывающую задачу).

На втором этапе реализация select() затем приостанавливает вызывающий объект до тех пор, пока какое-либо из событий не станет истинным. (См. fs/select.c.)

person Jörg Faschingbauer    schedule 30.04.2020
comment
Я вижу, ваше объяснение и пример мне очень помогли, и спасибо! Итак, похоже, что он проходит через функции (системный вызов sys_ppoll) -> do_sys_poll -> do_pol l-> do_polfd -> (f_op-›poll) . Я нашел в do_sys_poll, есть poll_initwait(..) и там функция qproc инициализируется функцией __pollwait. Функция and__pollwait помещает процесс в очередь ожидания. ах, система Linux слишком сложна :) - person Chan Kim; 30.04.2020