GNU/Linux >> Tutoriales Linux >  >> Linux

¿Cómo agregar la función de encuesta al código del módulo del kernel?

Ejemplo ejecutable mínimo

GitHub upstream con QEMU + Buildroot repetitivo:

  • módulo del núcleo poll.ko
  • poll.out prueba de espacio de usuario

En este ejemplo simplificado, generamos eventos de encuesta a partir de un hilo separado. En la vida real, los eventos de sondeo probablemente se desencadenarán por interrupciones, cuando el hardware haya terminado algún trabajo y haya nuevos datos disponibles para que los lean los usuarios.

El punto principal a recordar es que si poll devuelve cero, el kernel lo vuelve a llamar:¿Por qué necesitamos llamar a poll_wait en poll?

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

sondeo.fuera usuario:

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

Uso:

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

Resultado:jiffies se imprime en la salida estándar cada segundo desde el espacio del usuario, por ejemplo:

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

Forzar la encuesta file_operation para devolver 0 para ver lo que sucede más claramente:

insmod poll.ko ret0=1

Salida de muestra:

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

A partir de esto, vemos que el control no se devuelve a la zona del usuario:el núcleo sigue llamando a la encuesta file_operation una y otra vez.

Probado en Linux 5.4.3.


Puede encontrar algunos buenos ejemplos en el mismo kernel. Echa un vistazo a los siguientes archivos:

  • controladores/rtc/dev.c, controladores/rtc/interfaz.c
  • núcleo/printk/printk.c
  • controladores/char/random.c

Para agregar poll() función a su código, siga los siguientes pasos.

  1. Incluya los encabezados necesarios:

     #include <linux/wait.h>
     #include <linux/poll.h>
    
  2. Declarar variable de cola de espera:

     static DECLARE_WAIT_QUEUE_HEAD(fortune_wait);
    
  3. Agregar fortune_poll() y agréguela (como .poll devolución de llamada) a la estructura de operaciones de su archivo:

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

    Tenga en cuenta que debe devolver POLLIN | POLLRDNORM si tiene algunos datos nuevos para leer, y 0 en caso de que no haya nuevos datos para leer (poll() llamada agotada). Vea la encuesta man 2 para más detalles.

  4. Notifique a su cola de espera una vez que tenga nuevos datos:

     wake_up_interruptible(&fortune_wait);
    

Eso es lo básico sobre la implementación de poll() operación. Dependiendo de su tarea, es posible que deba usar alguna API de cola de espera en su .read función (como wait_event_interruptible() ).

Consulte también la pregunta relacionada:Implementación de encuestas en un módulo del kernel de Linux.


Linux
  1. ¿Cómo agregar una nueva línea al final de un archivo?

  2. Linux:¿cómo determinar qué módulo contamina el kernel?

  3. Linux:¿cómo recargar correctamente un módulo del kernel?

  4. Cómo llamar a la función C en C++, función C++ en C (Mezclar C y C++)

  5. ¿Cómo encontrar la versión de un módulo kernel compilado?

Cómo el kernel de Linux maneja las interrupciones

Cómo cargar o descargar un módulo del kernel de Linux

Cómo verificar la versión del kernel en Linux

Cómo agregar texto al comienzo del archivo en Linux

Cómo agregar una aplicación al Dock en el sistema operativo elemental

¿Cuál es la secuencia de carga del módulo del kernel de Linux al inicio? ¿Cómo se les da prioridad?