Forum Linux.noyau Ecrire un module avec un read bloquant

Posté par  .
Étiquettes : aucune
0
19
jan.
2010
Bonjour,

Je suis débutant dans l'écriture de module linux.

J'essaye de faire une interface d'échange entre kernel et userland qui soit bloquant pour le user tant que le kernel n'a pas la donnée de disponible.

Je reprends , j'ai donc ceci

static struct file_operations toto_fops = {
.owner = THIS_MODULE,
.read = toto_read,
.write = toto_write,
.open = toto_open,
.release = toto_release,
};

static ssize_t toto_read(struct file *file, char *buf, size_t count, loff_t *ppos)
{
....
wait_data()
copy_to_user(buf, (int *)videomemory + (int)*ppos, lus);
.....
}

Le problème est donc de faire le wait_data() . Je sais dans une autre portion du module quand cette donnée est prête mais ceci est indépendant de l'application qui a fait le open.

Je pense qu'il faut utiliser les spin_lock mais j'ai du mal a voir comment.

Je suis juste dans un cas un peu plus compliqué que les exemples données sur Internet et je me dit que la route est longue pour bien tout comprendre ;-)
  • # Synchro

    Posté par  (site web personnel) . Évalué à 2.

    La réponse est un peu partout dans le noyau, ou là : http://lwn.net/Kernel/LDD3/ ou encore là : http://sos.enix.org/ ... Il y a ce qu'on appelle des primitives de synchronisation dans un noyau (genre mutex, sémaphore, waitqueue, conditions parmi bien d'autres). Ces primitives peuvent permettre par exemple (au hasard) à une interruption de réveiller un thread en attente. Utiliser uniquement des spinlocks n'est pas la bonne solution (ne fait rien en UP, monopolise un CPU en multipro) : elle sera peut-être nécessaire mais certainement pas suffisante.
    • [^] # Re: Synchro

      Posté par  . Évalué à 1.

      Merci, je vais regarder plus profondément ces docs.

      En effet, je viens de freezer ma machine en tentant une synchro a coup de mutex_lock ;-)


      Le LDD3 j'avais vu mais c'est lourd de tout prendre d'un coup...
      Quoique je viens de voir que dans le chapitre 6 (Advanced char driver) il y a un paragraphe sur blocking I/O qui semble correspondre à mon besoin.

      Merci
      • [^] # Re: Synchro

        Posté par  (site web personnel) . Évalué à 2.

        Bloquer dans le kernel est rarement une bonne idée. Typiquement ta tache devient intuable.

        Tu devrais faire une attente limité puis retourner une erreur si un flag n'est pas mis.

        "La première sécurité est la liberté"

  • # Et la fonction est...

    Posté par  . Évalué à 4.

    wait_event_interruptible.

    Il faut que tu déclares un wake_queue_head dans ton module. Dans la fonction toto_read, tu mets le processus appelant en attente avec wait_event_interruptible. Et dans ton interruption, enfin là ou tu sais que tes données sont disponibles tu appelles wake_up_interruptible pour réveiller le processus bloqué.

    Ces macros sont définies dans include/linux/wait.h dans les sources du noyau. Utiliser les versions interruptibles, ça permet de quitter le processus en attente d'un simple Ctrl-C plutôt que de devoir redémarrer la machine lorsque les choses tournent mal (IT qui n'arrive pas, défaut matériel...).

    Je te conseille de passer une demi-journée voire une journée à lire le Linux Device Driver volume 3, ce n'est pas du temps perdu.
  • # Ca fonctionne

    Posté par  . Évalué à 2.

    j'ai finalement réussi a faire un squelette de ma solution, enfin je pense.

    un module test1 reçoit de la donné depuis le userland , le copie dans un buffer d'un module test2 qui peut être lu par le userland. l'application bloque tant qu'il n'y pas de donné.

    J'ai fait deux modules de type filesystem. Les deux ont chacun un buffer alloué par vmalloc (de même taille).
    le module test2 dispose d'une méthode test2_memcpy() qui est déclaré dans un fichier .h

    dans test1.c
    static struct file_operations test1_ops= {
    ....
    .write = test1_write,
    ...
    };


    static ssize_t test1_write(struct file *file, const char *buf, size_t
    count, loff_t *ppos) {
    .....
    copy_from_user((int *)buffer1 + (int)*ppos, buf, ecrits);
    ...
    if( test2_memcpy()(buffer1,buffer1_size) != 0)
    {
    printk(KERN_DEBUG "test2_memcpy failed\n");
    }
    ......
    }

    Dans test2.c:
    static DECLARE_WAIT_QUEUE_HEAD(wq);
    static int flag = 0;
    static struct file_operations test2_ops= {
    ....
    .read = test2_read,
    ...
    };
    static ssize_t test2_read(struct file *file, char *buf, size_t count,
    loff_t *ppos) {
    ...
    wait_event_interruptible(wq, flag != 0);
    flag = 0;
    copy_to_user(buf, (int *)buffer2 + (int)*ppos, lus);
    .....
    };

    int test2_memcpy(char *buf, size_t count)
    {
    buffer2 != memcpy(buffer2, buf, count);
    flag = 1;
    wake_up_interruptible(&wq);
    return 0;
    }
    EXPORT_SYMBOL( test2_memcpy);


    Et ca marche ;-)

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.