Maison  >  Article  >  Tutoriel système  >  Gestion des périphériques dans les systèmes Linux : commencer par la fonction dev_add

Gestion des périphériques dans les systèmes Linux : commencer par la fonction dev_add

PHPz
PHPzavant
2024-02-11 13:57:26945parcourir

La gestion des appareils Linux est un mécanisme important du système Linux. Elle est utilisée pour obtenir une gestion et un fonctionnement unifiés des appareils, tels que la création, la suppression, la recherche, la modification, etc. La gestion des appareils Linux implique trois concepts fondamentaux : kobject, kset et kobj_type. Un kobject est un objet noyau à usage général qui peut être utilisé pour représenter tout type de périphérique. kset est un objet conteneur qui peut être utilisé pour organiser et gérer un groupe de kobjects associés. kobj_type est un objet de type qui peut être utilisé pour définir les propriétés et les opérations de kobject.

Ici, nous allons discuter de la façon dont le noyau Linux (en prenant le noyau 4.8.5 comme exemple) gère les périphériques de caractères. C'est-à-dire que lorsque nous obtenons le numéro de périphérique, attribuons la structure cdev, enregistrons l'ensemble de méthodes de fonctionnement du pilote, et enfin Lorsque cdev_add(), qu'est-ce qui est exactement dit au noyau et comment le noyau gère-t-il ma structure cdev C'est ce dont parlera cet article ? Nous savons que la gestion des périphériques du noyau Linux est basée sur kobject, comme le montre notre structure cdev. Ainsi, vous verrez ensuite les fonctions d'exploitation des périphériques de caractères implémentées dans "fs/char_dev.c". Elles sont toutes basées sur. les fonctions qui opèrent sur kobject dans **"lib/kobject.c" et "drivers/base/map.c". D'accord, maintenant nous commençons par cdev_add()** et parcourons-le couche par couche.

objet cdev_map

//fs/char_dev.c
 27 static struct kobj_map *cdev_map;

L'implémentation des fonctions opérationnelles du périphérique de caractères dans le noyau est placée dans **"fs/char_dev.c" Lorsque vous ouvrez ce fichier, la première chose que vous remarquez est cette variable globale statique qui n'est pas courante dans le noyau. cdev_map**(27) , nous savons que pour améliorer la cohésion des logiciels, le noyau Linux essaie d'éviter d'utiliser des variables globales comme moyen de transférer des données entre fonctions lors de la conception, et il est recommandé d'utiliser plus souvent des listes de paramètres formelles , et cette variable de structure est utilisée partout dans ce fichier, il devrait donc s'agir d'une sorte d'information qui décrit tous les périphériques de caractères du système. Avec cette idée, nous pouvons trouver la définition de la structure kobj_map** dans **. "drivers/base/map.c" :

//drivers/base/map.c
 19 struct kobj_map {        
 20         struct probe {
 21                 struct probe *next;
 22                 dev_t dev;
 23                 unsigned long range;
 24                 struct module *owner;
 25                 kobj_probe_t *get;
 26                 int (*lock)(dev_t, void *);
 27                 void *data;
 28         } *probes[255];  
 29         struct mutex *lock;
 30 };

On peut voir de là que le noyau de kobj_map est un tableau de pointeurs de sonde struct de type et de taille 255. Dans cette structure de sonde, le premier membre next(21) connecte évidemment ces structures de sonde sous la forme d'une liste chaînée . Recherchez, le membre dev du type dev_t est évidemment le numéro de l'appareil, get(25) et lock(26) sont respectivement deux interfaces de fonction. Le dernier point est ici, void* est une huile de serpent. tapez en langage C, voici notre structure cdev (comme le montre l'analyse ultérieure), donc ce cdev_map est un pointeur de type struct kobj_map, qui contient un tableau de type de pointeur de sonde struct et de taille 255, chaque élément de le tableau La structure de sonde pointée encapsule un numéro de périphérique et l'objet de périphérique correspondant (voici cdev). On peut voir que ce cdev_map encapsule toutes les structures cdev et les numéros de périphérique correspondants dans le système, avec un maximum de 255 périphériques de caractères . .
Gestion des périphériques dans les systèmes Linux : commencer par la fonction dev_add

cdev_add

Après avoir compris les fonctions de cdev_map, nous pouvons explorer cdev_add()

//fs/char_dev.c
456 int cdev_add(struct cdev *p, dev_t dev, unsigned count)  
457 {
458         int error;
459 
460         p->dev = dev;
461         p->count = count;
462 
463         error = kobj_map(cdev_map, dev, count, NULL,
464                          exact_match, exact_lock, p);
465         if (error)
466                 return error;
467 
468         kobject_get(p->kobj.parent);
469 
470         return 0;
471 }

La fonction est très courte, (460-461) consiste à remplir le numéro d'appareil et la longueur du numéro d'appareil que nous avons obtenus auparavant dans la structure cdev, kobject_get()(468) ne fait rien :

//lib/kobject.c 
 591 struct kobject *kobject_get(struct kobject *kobj)    
 592 {      
 593         if (kobj) {
             ...
 598                 kref_get(&kobj->kref);
 599         }
 600         return kobj;
 601 }

Donc, le travail de base est évidemment confié à kobj_map()

kobj_map()

Cette fonction occupe une position importante dans la gestion des périphériques du noyau. Ici, nous analysons sa fonction uniquement du point de vue des périphériques de caractères et la implémentons en premier

.
//drivers/base/map.c
 32 int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range,
 33              struct module *module, kobj_probe_t *probe,
 34              int (*lock)(dev_t, void *), void *data)
 35 {
 36         unsigned n = MAJOR(dev + range - 1) - MAJOR(dev) + 1;
 37         unsigned index = MAJOR(dev);
 38         unsigned i;
 39         struct probe *p;
        ...
 44         p = kmalloc_array(n, sizeof(struct probe), GFP_KERNEL);
        ...
 48         for (i = 0; i owner = module;
 50                 p->get = probe;
 51                 p->lock = lock;
 52                 p->dev = dev;
 53                 p->range = range;
 54                 p->data = data;
 55         }
 56         mutex_lock(domain->lock);
 57         for (i = 0, p -= n; i probes[index % 255];
 59                 while (*s && (*s)->range next;
 61                 p->next = *s;       
 62                 *s = p;
 63         }
 64         mutex_unlock(domain->lock);      
 65         return 0;
 66 }

La conception de cette fonction est également très simple, c'est-à-dire qu'elle encapsule une structure de sonde et met son adresse dans le tableau de sondes, puis l'encapsule dans cdev_map, (48-55)j consiste à placer l'appareil en fonction du numéro des numéros de périphérique transmis Le numéro et le cdev sont encapsulés dans n structures de sonde allouées tour à tour par kmalloc_array (57-63). Il s'agit de parcourir le tableau probs jusqu'à ce qu'un élément avec une valeur NULL soit trouvé, puis de stocker l'adresse de la sonde. dans des sondes. À ce stade, nous avons placé notre cdev dans la structure de données du noyau. Bien entendu, un grand nombre d'attributs de cdev sont remplis par le noyau pour nous.

chrdev_open()

将设备放入的内核,我们再来看看内核是怎么找到一个特定的cdev的,对一个字符设备的访问流程大概是:文件路径=>inode=>chrdev_open=>cdev->fops->my_chr_open。所以只要通过VFS找到了inode,就可以找到chrdev_open(),这里我们就来关注一个chrdev_open()是怎么从内核的数据结构中找到我们的cdev并回调里满的my_chr_open()的。首先,chrdev_open()尝试将inode->i_cdev(一个cdev结构指针)保存在局部变量p中(359),如果p为空,即inode->i_cdev为空(360),我们就根据inode->i_rdev(设备号)通过kobj_lookup搜索cdev_map,并返回与之对应kobj(364),由于kobject是cdev的父类,我们根据container_of很容易找到相应的cdev结构并将其保存在inode->i_cdev中(367),找到了cdev,我们就可以将inode->devices挂接到inode->i_cdev的管理链表中,这样下次就不用重新搜索,直接cdev_get()即可(378)。找到了我们的cdev结构,我们就可以将其中的操作方法集inode->i_cdev->ops传递给filp->f_ops(386-390),这样,我们就可以回调我们的设备打开函数my_chr_open()(392);

//fs/char_dev.c
326 static struct kobject *cdev_get(struct cdev *p)     
327 {
328         struct module *owner = p->owner;
329         struct kobject *kobj;
330         
331         if (owner && !try_module_get(owner))
332                 return NULL;
333         kobj = kobject_get(&p->kobj);
        ...
336         return kobj;
337 }

351 static int chrdev_open(struct inode *inode, struct file *filp)
352 {
353         const struct file_operations *fops;
354         struct cdev *p;
355         struct cdev *new = NULL;
356         int ret = 0;
        ...
359         p = inode->i_cdev;
360         if (!p) {
361                 struct kobject *kobj;
362                 int idx;
            ...
364                 kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
            ...
367                 new = container_of(kobj, struct cdev, kobj);
369                 /* Check i_cdev again in case somebody beat us to it while
370                    we dropped the lock. */
371                 p = inode->i_cdev;
372                 if (!p) {
373                         inode->i_cdev = p = new;
374                         list_add(&inode->i_devices, &p->list);
375                         new = NULL;
376                 } else if (!cdev_get(p))
377                         ret = -ENXIO;
378         } else if (!cdev_get(p))
379                 ret = -ENXIO;
            ...
386         fops = fops_get(p->ops);
        ...
390         replace_fops(filp, fops);
391         if (filp->f_op->open) {
392                 ret = filp->f_op->open(inode, filp);
            ...
395         }
396 
397         return 0;
398 
399  out_cdev_put:
400         cdev_put(p);
401         return ret;
402 }

通过本文,我们了解了Linux设备管理的原理和方法。它们可以用来实现对设备的统一管理和操作。我们应该根据实际需求选择合适的方法,并遵循一些基本原则,如使用正确的引用计数,使用正确的锁机制,使用正确的属性文件等。Linux设备管理是Linux系统中最基本的机制之一,它可以实现对设备的抽象和封装,也可以提升系统的可维护性和可扩展性。希望本文能够对你有所帮助和启发。

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer