GNU/Linux >> Tutoriels Linux >  >> Linux

Comment ajouter la fonction d'interrogation au code du module du noyau ?

Exemple exécutable minimal

GitHub en amont avec QEMU + Buildroot standard :

  • Module noyau poll.ko
  • Test utilisateur poll.out

Dans cet exemple simplifié, nous générons des événements d'interrogation à partir d'un thread séparé. Dans la vraie vie, les événements d'interrogation seront probablement déclenchés par des interruptions, lorsque le matériel a terminé une tâche et que de nouvelles données sont devenues disponibles pour que l'espace utilisateur puisse les lire.

Le point principal à retenir est que si poll renvoie zéro, le noyau l'appelle à nouveau :Pourquoi devons-nous appeler poll_wait dans 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");

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

Utilisation :

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

Résultat :jiffies est imprimé sur stdout chaque seconde depuis userland, par exemple :

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

Forcer le sondage file_operation pour retourner 0 pour voir ce qui se passe plus clairement :

insmod poll.ko ret0=1

Exemple de sortie :

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

À partir de là, nous voyons que le contrôle n'est pas rendu à l'espace utilisateur :le noyau ne cesse d'appeler le sondage file_operation encore et encore.

Testé sur Linux 5.4.3.


Vous pouvez trouver quelques bons exemples dans le noyau lui-même. Jetez un œil aux fichiers suivants :

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

Pour ajouter poll() fonction à votre code suivez les étapes suivantes.

  1. Inclure les en-têtes nécessaires :

     #include <linux/wait.h>
     #include <linux/poll.h>
    
  2. Déclarez la variable de la file d'attente :

     static DECLARE_WAIT_QUEUE_HEAD(fortune_wait);
    
  3. Ajouter fortune_poll() fonction et ajoutez-la (comme .poll callback) à votre structure d'opérations sur les fichiers :

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

    Notez que vous devez renvoyer POLLIN | POLLRDNORM si vous avez de nouvelles données à lire, et 0 s'il n'y a pas de nouvelles données à lire (poll() appel expiré). Voir le sondage de l'homme 2 pour plus de détails.

  4. Avertissez votre file d'attente dès que vous avez de nouvelles données :

     wake_up_interruptible(&fortune_wait);
    

C'est la base de l'implémentation de poll() opération. Selon votre tâche, vous devrez peut-être utiliser une API de file d'attente dans votre .read fonction (comme wait_event_interruptible() ).

Voir aussi la question connexe :Implémentation d'un sondage dans un module du noyau Linux.


Linux
  1. Comment trouver le module du noyau pour un appareil donné ?

  2. Linux - Comment déterminer quel module corrompt le noyau ?

  3. Linux - Comment recharger correctement un module du noyau ?

  4. Comment utiliser ioctl() pour manipuler mon module noyau ?

  5. Comment charger les modules du noyau Linux à partir du code C ?

Comment le noyau Linux gère les interruptions

Comment charger ou décharger un module du noyau Linux

Comment vérifier la version du noyau sous Linux

Comment ajouter du texte au début du fichier sous Linux

Comment ajouter une application au Dock dans un système d'exploitation élémentaire

Quelle est la séquence de chargement du module du noyau Linux au démarrage ? Quelle priorité leur est accordée ?