GNU/Linux >> Linux Esercitazione >  >> Linux

Come aggiungere la funzione poll al codice del modulo del kernel?

Esempio eseguibile minimo

GitHub a monte con QEMU + boilerplate Buildroot:

  • modulo kernel poll.ko
  • test utente di poll.out

In questo esempio semplificato, generiamo eventi di sondaggio da un thread separato. Nella vita reale, gli eventi di poll saranno probabilmente attivati ​​da interruzioni, quando l'hardware ha terminato un lavoro e nuovi dati diventano disponibili per la lettura da parte dell'utente.

Il punto principale da ricordare è che se poll restituisce zero, il kernel lo chiama di nuovo:Perché dobbiamo chiamare poll_wait in poll?

poll.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");

area utente poll.out:

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

Utilizzo:

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

Risultato:jiffies viene stampato su stdout ogni secondo da userland, ad esempio:

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

Forza il sondaggio file_operation per restituire 0 per vedere cosa succede più chiaramente:

insmod poll.ko ret0=1

Esempio di output:

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

Da questo vediamo che il controllo non viene restituito a userland:il kernel continua semplicemente a chiamare il sondaggio file_operation ancora e ancora.

Testato su Linux 5.4.3.


Puoi trovare alcuni buoni esempi nel kernel stesso. Dai un'occhiata ai prossimi file:

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

Per aggiungere poll() funzione al tuo codice segui i passaggi successivi.

  1. Includi le intestazioni necessarie:

     #include <linux/wait.h>
     #include <linux/poll.h>
    
  2. Dichiara variabile waitqueue:

     static DECLARE_WAIT_QUEUE_HEAD(fortune_wait);
    
  3. Aggiungi fortune_poll() funzione e aggiungerla (come .poll callback) alla struttura delle operazioni sui file:

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

    Nota che dovresti restituire POLLIN | POLLRDNORM se hai nuovi dati da leggere, e 0 nel caso in cui non ci siano nuovi dati da leggere (poll() chiamata scaduta). Vedi il sondaggio man 2 per i dettagli.

  4. Invia una notifica alla tua coda di attesa una volta che hai nuovi dati:

     wake_up_interruptible(&fortune_wait);
    

Queste sono le cose di base sull'implementazione di poll() operazione. A seconda della tua attività, potrebbe essere necessario utilizzare alcune API waitqueue nel tuo .read funzione (come wait_event_interruptible() ).

Vedi anche domanda correlata:Implementazione del sondaggio in un modulo del kernel Linux.


Linux
  1. Come aggiungere una nuova riga alla fine di un file?

  2. Come trovare il modulo del kernel per un determinato dispositivo?

  3. Linux:come determinare quale modulo contamina il kernel?

  4. Linux:come ricaricare correttamente un modulo del kernel?

  5. Come aggiungere i parametri del modulo del kernel?

Come il kernel Linux gestisce gli interrupt

Come caricare o scaricare un modulo del kernel Linux

Come controllare la versione del kernel in Linux

Come aggiungere testo all'inizio del file in Linux

Come aggiungere un'applicazione al Dock nel sistema operativo elementare

Come trovare i chiamanti e il chiamato di una funzione nel codice C in vi/vim?