GNU/Linux >> Znalost Linux >  >> Linux

Jak přidat funkci dotazování do kódu modulu jádra?

Minimální spustitelný příklad

Upstream GitHub s QEMU + základním prvkem Buildroot:

  • modul jádra poll.ko
  • poll.out userland test

V tomto zjednodušeném příkladu generujeme události dotazování ze samostatného vlákna. V reálném životě budou události dotazování pravděpodobně vyvolány přerušeními, když hardware dokončí nějakou práci a nová data budou dostupná pro uživatele ke čtení.

Hlavní bod k zapamatování je, že pokud poll vrátí nulu, jádro ji zavolá znovu:Proč musíme v poll volat poll_wait?

anketa.ko

#include <linux/debugfs.h>
#include <linux/delay.h> /* usleep_range */
#include <linux/errno.h> /* EFAULT */
#include <linux/fs.h>
#include <linux/jiffies.h>
#include <linux/kernel.h> /* min */
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/printk.h> /* printk */
#include <linux/uaccess.h> /* copy_from_user, copy_to_user */
#include <linux/wait.h> /* wait_queue_head_t, wait_event_interruptible, wake_up_interruptible  */
#include <uapi/linux/stat.h> /* S_IRUSR */

static int ret0 = 0;
module_param(ret0, int, S_IRUSR | S_IWUSR);
MODULE_PARM_DESC(i, "if 1, always return 0 from poll");

static char readbuf[1024];
static size_t readbuflen;
static struct dentry *debugfs_file;
static struct task_struct *kthread;
static wait_queue_head_t waitqueue;

static ssize_t read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{
    ssize_t ret;
    if (copy_to_user(buf, readbuf, readbuflen)) {
        ret = -EFAULT;
    } else {
        ret = readbuflen;
    }
    /* This is normal pipe behaviour: data gets drained once a reader reads from it. */
    /* https://stackoverflow.com/questions/1634580/named-pipes-fifos-on-unix-with-multiple-readers */
    readbuflen = 0;
    return ret;
}

/* If you return 0 here, then the kernel will sleep until an event
 * happens in the queue. and then call this again, because of the call to poll_wait. */
unsigned int poll(struct file *filp, struct poll_table_struct *wait)
{
    pr_info("poll\n");
    /* This doesn't sleep. It just makes the kernel call poll again if we return 0. */
    poll_wait(filp, &waitqueue, wait);
    if (readbuflen && !ret0) {
        pr_info("return POLLIN\n");
        return POLLIN;
    } else {
        pr_info("return 0\n");
        return 0;
    }
}

static int kthread_func(void *data)
{
    while (!kthread_should_stop()) {
        readbuflen = snprintf(
            readbuf,
            sizeof(readbuf),
            "%llu",
            (unsigned long long)jiffies
        );
        usleep_range(1000000, 1000001);
        pr_info("wake_up\n");
        wake_up(&waitqueue);
    }
    return 0;
}

static const struct file_operations fops = {
    .owner = THIS_MODULE,
    .read = read,
    .poll = poll
};

static int myinit(void)
{
    debugfs_file = debugfs_create_file(
        "lkmc_poll", S_IRUSR | S_IWUSR, NULL, NULL, &fops);
    init_waitqueue_head(&waitqueue);
    kthread = kthread_create(kthread_func, NULL, "mykthread");
    wake_up_process(kthread);
    return 0;
}

static void myexit(void)
{
    kthread_stop(kthread);
    debugfs_remove(debugfs_file);
}

module_init(myinit)
module_exit(myexit)
MODULE_LICENSE("GPL");

poll.out userland:

#define _XOPEN_SOURCE 700
#include <assert.h>
#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(int argc, char **argv) {
    char buf[1024];
    int fd, i, n;
    short revents;
    struct pollfd pfd;

    if (argc < 2) {
        fprintf(stderr, "usage: %s <poll-device>\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    fd = open(argv[1], O_RDONLY | O_NONBLOCK);
    if (fd == -1) {
        perror("open");
        exit(EXIT_FAILURE);
    }
    pfd.fd = fd;
    pfd.events = POLLIN;
    while (1) {
        puts("poll");
        i = poll(&pfd, 1, -1);
        if (i == -1) {
            perror("poll");
            assert(0);
        }
        revents = pfd.revents;
        printf("revents = %d\n", revents);
        if (revents & POLLIN) {
            n = read(pfd.fd, buf, sizeof(buf));
            printf("POLLIN n=%d buf=%.*s\n", n, n, buf);
        }
    }
}

Použití:

insmod poll.ko
mount -t debugfs none /sys/kernel/debug
./kernel_modules/poll.out /sys/kernel/debug/lkmc_poll

Výsledek:jiffies se vytiskne na stdout každou sekundu z uživatelské země, např.:

poll
<6>[    4.275305] poll
<6>[    4.275580] return POLLIN
revents = 1
POLLIN n=10 buf=4294893337
poll
<6>[    4.276627] poll
<6>[    4.276911] return 0
<6>[    5.271193] wake_up
<6>[    5.272326] poll
<6>[    5.273207] return POLLIN
revents = 1
POLLIN n=10 buf=4294893588
poll
<6>[    5.276367] poll
<6>[    5.276618] return 0
<6>[    6.275178] wake_up
<6>[    6.276370] poll
<6>[    6.277269] return POLLIN
revents = 1
POLLIN n=10 buf=4294893839

Vynutit průzkum file_operation vrátit 0, abyste viděli, co se stane jasněji:

insmod poll.ko ret0=1

Ukázkový výstup:

poll
<6>[   85.674801] poll
<6>[   85.675788] return 0
<6>[   86.675182] wake_up
<6>[   86.676431] poll
<6>[   86.677373] return 0
<6>[   87.679198] wake_up
<6>[   87.680515] poll
<6>[   87.681564] return 0
<6>[   88.683198] wake_up

Z toho vidíme, že kontrola není vrácena do uživatelské země:jádro jen stále volá poll file_operation znovu a znovu.

Testováno na Linuxu 5.4.3.


Některé dobré příklady můžete najít v samotném jádře. Podívejte se na další soubory:

  • drivers/rtc/dev.c, drivers/rtc/interface.c
  • kernel/printk/printk.c
  • drivers/char/random.c

Chcete-li přidat poll() postupujte podle následujících kroků.

  1. Zahrňte potřebná záhlaví:

     #include <linux/wait.h>
     #include <linux/poll.h>
    
  2. Deklarujte proměnnou fronty čekání:

     static DECLARE_WAIT_QUEUE_HEAD(fortune_wait);
    
  3. Přidejte fortune_poll() funkci a přidejte ji (jako .poll callback) do struktury operací se soubory:

     static unsigned int fortune_poll(struct file *file, poll_table *wait)
     {
         poll_wait(file, &fortune_wait, wait);
         if (new-data-is-ready)
             return POLLIN | POLLRDNORM;
         return 0;
     }
    
     static const struct file_operations proc_test_fops = {
         ....
         .poll = fortune_poll,
     };
    

    Všimněte si, že byste měli vrátit POLLIN | POLLRDNORM pokud máte nějaká nová data ke čtení, a 0 v případě, že neexistují žádná nová data ke čtení (poll() vypršel časový limit hovoru). Podrobnosti naleznete v anketě muže 2.

  4. Upozorněte svou frontu, jakmile budete mít nová data:

     wake_up_interruptible(&fortune_wait);
    

To je základní informace o implementaci poll() úkon. V závislosti na vaší úloze může být nutné použít nějaké rozhraní API pro čekání na vašem .read funkce (jako wait_event_interruptible() ).

Viz také související otázka:Implementace poll v modulu jádra Linuxu.


Linux
  1. Jak přidat nový řádek na konec souboru?

  2. Jak najít modul jádra pro dané zařízení?

  3. Linux – Jak zjistit, který modul poškozuje jádro?

  1. Linux – proprietární nebo uzavřené části jádra?

  2. Jak volat funkci C v C++, funkci C++ v C (Mix C a C++)

  3. Jak zjistit verzi kompilovaného modulu jádra?

  1. Jak načíst moduly jádra Linuxu z kódu C?

  2. Jak zkontrolovat HZ v terminálu?

  3. Jaká je sekvence načítání modulu linuxového jádra při spuštění? Jak je jim nastavena priorita?