Maison  >  Article  >  Tutoriel système  >  Explication détaillée du modèle de périphérique Linux (4)_sysfs

Explication détaillée du modèle de périphérique Linux (4)_sysfs

王林
王林avant
2024-02-15 17:00:03598parcourir

1.Introduction

sysfs est un système de fichiers basé sur la RAM qui est utilisé conjointement avec Kobject pour exporter les structures de données et les attributs du noyau vers l'espace utilisateur, fournissant ainsi une prise en charge de l'accès à ces structures de données sous la forme de structures de répertoires de fichiers.

Explication détaillée du modèle de périphérique Linux (4)_sysfs

sysfs possède toutes les propriétés d'un système de fichiers, mais cet article se concentre sur ses fonctionnalités dans le modèle de périphérique Linux. Par conséquent, nous n'aborderons pas trop de détails sur l'implémentation du système de fichiers, mais présenterons uniquement le rôle et l'utilisation de sysfs dans le modèle de périphérique. Incluez spécifiquement :

  • La relation entre sysfs et Kobject
  • Le concept d'attribut
  • Interface d'exploitation du système de fichiers de sysfs

2. La relation entre sysfs et Kobject

Dans l'article "Linux Device Model_Kobject", il est mentionné que chaque Kobject correspond à un répertoire dans sysfs. Par conséquent, lors de l'ajout de Kobject au noyau, l'interface create_dir appellera l'interface create directory du système de fichiers sysfs pour créer un répertoire correspondant au Kobject. Le code pertinent est le suivant :

.
 1: /* lib/kobject.c, line 47 */
 2: static int create_dir(struct kobject *kobj)
 3: {
 4:     int error = 0;
 5:     error = sysfs_create_dir(kobj);
 6:     if (!error) {
 7:         error = populate_dir(kobj);
 8:     if (error)
 9:         sysfs_remove_dir(kobj);
 10:     }   
 11:     return error;
 12: }
 13:  
 14: /* fs/sysfs/dir.c, line 736 */
 15: **
 16: *  sysfs_create_dir - create a directory for an object.
 17: *  @kobj:      object we're creating directory for. 
 18: */
 19: int sysfs_create_dir(struct kobject * kobj)
 20: {
 21:     enum kobj_ns_type type;
 22:     struct sysfs_dirent *parent_sd, *sd;
 23:     const void *ns = NULL;
 24:     int error = 0;
 25:     ...
 26: }

3. attribut

3.1 Présentation de la fonction d'attribut

Dans sysfs, pourquoi y a-t-il la notion d'attribut ? En fait, cela correspond à kobject, faisant référence aux « attributs » de kobject. Nous savons,

Le répertoire dans sysfs décrit kobject, et kobject est l'incarnation d'une variable de type de données spécifique (telle qu'un périphérique struct). Par conséquent, les attributs de kobject sont les attributs de ces variables. Cela peut être n'importe quoi, un nom, une variable interne, une chaîne, etc. Les attributs sont fournis sous forme de fichiers dans le système de fichiers sysfs, c'est-à-dire que tous les attributs d'un kobject sont présentés sous forme de fichiers dans son répertoire sysfs correspondant. Ces fichiers sont généralement lisibles et inscriptibles, et les modules du noyau qui définissent ces attributs enregistreront et renverront les valeurs de ces attributs en fonction des opérations de lecture et d'écriture dans l'espace utilisateur.

Pour résumer : ce qu'on appelle l'attribut est une méthode d'interaction d'informations entre l'espace noyau et l'espace utilisateur. Par exemple, si un pilote définit une variable et espère que le programme de l'espace utilisateur pourra modifier la variable pour contrôler le comportement d'exécution du pilote, la variable peut être ouverte en tant qu'attribut sysfs.

Dans le noyau Linux, les attributs sont divisés en attributs ordinaires et attributs binaires, comme suit :

 1: /* include/linux/sysfs.h, line 26 */
 2: struct attribute {
 3:     const char *name;
 4:     umode_t         mode;
 5: #ifdef CONFIG_DEBUG_LOCK_ALLOC
 6:     bool ignore_lockdep:1;
 7:     struct lock_class_key   *key;
 8:     struct lock_class_key   skey;
 9: #endif
 10: };
 11:  
 12: /* include/linux/sysfs.h, line 100 */
 13: struct bin_attribute {
 14:     struct attribute    attr;
 15:     size_t          size;
 16:     void *private;
 17:     ssize_t (*read)(struct file *, struct kobject *, struct bin_attribute *,
 18:                     char *, loff_t, size_t);
 19:     ssize_t (*write)(struct file *,struct kobject *, struct bin_attribute *,
 20:                     char *, loff_t, size_t);
 21:     int (*mmap)(struct file *, struct kobject *, struct bin_attribute *attr,
 22:                     struct vm_area_struct *vma);
 23: };
L'attribut

struct est un attribut courant. Le fichier sysfs généré à l'aide de cet attribut ne peut être lu et écrit que sous la forme d'une chaîne (j'expliquerai pourquoi plus tard). Sur la base de l'attribut struct, struct bin_attribute ajoute des fonctions de lecture, d'écriture et autres, de sorte que le fichier sysfs qu'il génère peut être lu et écrit de n'importe quelle manière.

Après avoir parlé des concepts de base, nous devons nous poser deux questions :

Comment le noyau transforme-t-il les attributs en fichiers dans sysfs ?

Comment transmettre les opérations de lecture et d'écriture sur les fichiers sysfs de l'espace utilisateur au noyau ?

Jetons un coup d’œil au processus ci-dessous.

3.2 Création d'un fichier d'attributs

Dans le noyau Linux, la création du fichier d'attributs est complétée par l'interface sysfs_create_file dans fs/sysfs/file.c. Il n'y a rien de spécial dans l'implémentation de cette interface. La plupart d'entre elles sont des opérations liées au système de fichiers et ne le font pas. ont beaucoup à voir avec le modèle de l'appareil. Relation, passons-le ici.

3.3 lecture et écriture de fichiers d'attributs

Quand nous voyons le prototype de l'attribut struct au chapitre 3.1, nous pouvons marmonner que la structure est très simple, name représente le nom du fichier, mode représente le mode fichier et les autres champs sont utilisés par le noyau pour déboguer Kernel Lock et file opérations. Où est l’interface ?

Ne vous inquiétez pas, allons dans le répertoire fs/sysfs pour examiner la logique du code lié à sysfs.

Tous les systèmes de fichiers définiront une variable struct file_operations pour décrire l'interface de fonctionnement de ce système de fichiers, et sysfs ne fait pas exception :

 1: /* fs/sysfs/file.c, line 472 */
 2: const struct file_operations sysfs_file_operations = {
 3:     .read       = sysfs_read_file,
 4:     .write      = sysfs_write_file,
 5:     .llseek     = generic_file_llseek,
 6:     .open       = sysfs_open_file,
 7:     .release    = sysfs_release,
 8:     .poll       = sysfs_poll,
 9: };

L'opération de lecture du fichier d'attributs sera transférée de VFS vers l'interface de lecture (c'est-à-dire sysfs_read_file) de sysfs_file_operations. Jetons un coup d'œil à la logique de traitement de cette interface.

 1: /* fs/sysfs/file.c, line 127 */
 2: static ssize_t
 3: sysfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos)
 4: {
 5:     struct sysfs_buffer * buffer = file->private_data;
 6:     ssize_t retval = 0;
 7:  
 8:     mutex_lock(&buffer->mutex);
 9:     if (buffer->needs_read_fill || *ppos == 0) {
 10:        retval = fill_read_buffer(file->f_path.dentry,buffer);
 11:        if (retval)
 12:            goto out;
 13:    }
 14: ...
 15: }
 16: /* fs/sysfs/file.c, line 67 */
 17: static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer)
 18: {           
 19:    struct sysfs_dirent *attr_sd = dentry->d_fsdata;
 20:    struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;
 21:    const struct sysfs_ops * ops = buffer->ops;
 22:    ...        
 23:    count = ops->show(kobj, attr_sd->s_attr.attr, buffer->page);
 24:    ...
 25: }

read处理看着很简单,sysfs_read_file从file指针中取一个私有指针(注:大家可以稍微留一下心,私有数据的概念,在VFS中使用是非常普遍的),转换为一个struct sysfs_buffer类型的指针,以此为参数(buffer),转身就调用fill_read_buffer接口。

而fill_read_buffer接口,直接从buffer指针中取出一个struct sysfs_ops指针,调用该指针的show函数,即完成了文件的read操作。

那么后续呢?当然是由ops->show接口接着处理咯。而具体怎么处理,就是其它模块(例如某个driver)的事了,sysfs不再关心(其实,Linux大多的核心代码,都是只提供架构和机制,具体的实现,也就是苦力,留给那些码农吧!这就是设计的魅力)。

不过还没完,这个struct sysfs_ops指针哪来的?好吧,我们再看看open(sysfs_open_file)接口吧。

 1: /* fs/sysfs/file.c, line 326 */
 2: static int sysfs_open_file(struct inode *inode, struct file *file)
 3: {
 4:     struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
 5:     struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;
 6:     struct sysfs_buffer *buffer;
 7:     const struct sysfs_ops *ops;
 8:     int error = -EACCES;
 9:  
 10:    /* need attr_sd for attr and ops, its parent for kobj */
 11:    if (!sysfs_get_active(attr_sd))
 12:    return -ENODEV;
 13:  
 14:    /* every kobject with an attribute needs a ktype assigned */
 15:    if (kobj->ktype && kobj->ktype->sysfs_ops)
 16:        ops = kobj->ktype->sysfs_ops;
 17:    else {
 18:        WARN(1, KERN_ERR "missing sysfs attribute operations for "
 19:            "kobject: %s\n", kobject_name(kobj));
 20:        goto err_out;
 21:    }
 22:  
 23:    ...
 24:  
 25:    buffer = kzalloc(sizeof(struct sysfs_buffer), GFP_KERNEL);
 26:    if (!buffer)
 27:        goto err_out;
 28:  
 29:    mutex_init(&buffer->mutex);
 30:    buffer->needs_read_fill = 1;
 31:    buffer->ops = ops;
 32:    file->private_data = buffer;
 33:    ...
 34: }

哦,原来和ktype有关系。这个指针是从该attribute所从属的kobject中拿的。再去看一下”Linux设备模型_Kobject”中ktype的定义,还真有一个struct sysfs_ops的指针。

我们注意一下14行的注释以及其后代码逻辑,如果从属的kobject(就是attribute文件所在的目录)没有ktype,或者没有ktype->sysfs_ops指针,是不允许它注册任何attribute的!

经过确认后,sysfs_open_file从ktype中取出struct sysfs_ops指针,并在随后的代码逻辑中,分配一个struct sysfs_buffer类型的指针(buffer),并把struct sysfs_ops指针保存在其中,随后(注意哦),把buffer指针交给file的private_data,随后read/write等接口便可以取出使用。嗯!惯用伎俩!

顺便看一下struct sysfs_ops吧,我想你已经能够猜到了。

 1: /* include/linux/sysfs.h, line 124 */
 2: struct sysfs_ops {
 3:     ssize_t (*show)(struct kobject *, struct attribute *,char *);
 4:     ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
 5:     const void *(*namespace)(struct kobject *, const struct attribute *);
 6: };

attribute文件的write过程和read类似,这里就不再多说。另外,上面只分析了普通attribute的逻辑,而二进制类型的呢?也类似,去看看fs/sysfs/bin.c吧,这里也不说了。

讲到这里,应该已经结束了,事实却不是如此。上面read/write的数据流,只到kobject(也就是目录)级别哦,而真正需要操作的是attribute(文件)啊!这中间一定还有一层转换!确实,不过又交给其它模块了。 下面我们通过一个例子,来说明如何转换的。

4. sysfs在设备模型中的应用总结

让我们通过设备模型class.c中有关sysfs的实现,来总结一下sysfs的应用方式。

首先,在class.c中,定义了Class所需的ktype以及sysfs_ops类型的变量,如下:

 1: /* drivers/base/class.c, line 86 */
 2: static const struct sysfs_ops class_sysfs_ops = {
 3:     .show      = class_attr_show,
 4:     .store     = class_attr_store,
 5:     .namespace = class_attr_namespace,
 6: };  
 7: 
 8: static struct kobj_type class_ktype = {
 9:     .sysfs_ops  = &class_sysfs_ops,
 10:    .release    = class_release,
 11:    .child_ns_type  = class_child_ns_type,
 12: };

由前面章节的描述可知,所有class_type的Kobject下面的attribute文件的读写操作,都会交给class_attr_show和class_attr_store两个接口处理。以class_attr_show为例:

 1: /* drivers/base/class.c, line 24 */
 2: #define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr)
 3:  
 4: static ssize_t class_attr_show(struct kobject *kobj, struct attribute *attr,
 5: char *buf)
 6: {   
 7:     struct class_attribute *class_attr = to_class_attr(attr);
 8:     struct subsys_private *cp = to_subsys_private(kobj);
 9:     ssize_t ret = -EIO;
 10:  
 11:    if (class_attr->show)
 12:    ret = class_attr->show(cp->class, class_attr, buf);
 13:    return ret;
 14: }

该接口使用container_of从struct attribute类型的指针中取得一个class模块的自定义指针:struct class_attribute,该指针中包含了class模块自身的show和store接口。下面是struct class_attribute的声明:

 1: /* include/linux/device.h, line 399 */
 2: struct class_attribute {
 3:     struct attribute attr;
 4:     ssize_t (*show)(struct class *class, struct class_attribute *attr,
 5:                     char *buf);
 6:     ssize_t (*store)(struct class *class, struct class_attribute *attr,
 7:                     const char *buf, size_t count);
 8:     const void *(*namespace)(struct class *class,
 9:                                 const struct class_attribute *attr); 
 10: };

因此,所有需要使用attribute的模块,都不会直接定义struct attribute变量,而是通过一个自定义的数据结构,该数据结构的一个成员是struct attribute类型的变量,并提供show和store回调函数。然后在该模块ktype所对应的struct sysfs_ops变量中,实现该本模块整体的show和store函数,并在被调用时,转接到自定义数据结构(struct class_attribute)

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