linux设备模型与sys文件系统

Posted feisonzl

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux设备模型与sys文件系统相关的知识,希望对你有一定的参考价值。

1.设备模型简介

随着linux的普及,硬件设备对linux的支持逐渐增减,导致linux内核近一半的代码是由驱动代码构成的,从而导致linux内核维护难,代码臃肿。因此内核开发人员为了规范内核,加快驱动开发进度,变引入了设备模型的概念。
设备模型的主要任务是:电源管理和系统关机、与用户空间通信,热插拔设备,设备类型。
设备模型的体现主要在sysfs中。

2.设备模型的基本概念

2.1 Bus,Class,Device,Device_Driver概念

Device:设备,表示具体设备(可以理解为该设备的属性),设备模型中用struct device表示,对应目录/sys/devices/*/*/.../。
struct device 
    struct device       *parent;
    struct device_private   *p; 
    struct kobject kobj;
    const char      *init_name; /* initial name of the device */
    const struct device_type *type;
    struct mutex        mutex;  /* mutex to synchronize calls to
                     * its driver.
                     */
    struct bus_type *bus;       /* type of bus device is on */
    struct device_driver *driver;   /* which driver has allocated this
                       device */
    void        *platform_data; /* Platform specific data, device
                       core doesn't touch it */
    struct dev_pm_info  power;
    struct dev_pm_domain    *pm_domain;
#ifdef CONFIG_NUMA
    int     numa_node;  /* NUMA node this device is close to */
#endif
    u64     *dma_mask;  /* dma mask (if dma'able device) */
    u64     coherent_dma_mask;/* Like dma_mask, but for
                         alloc_coherent mappings as
                         not all hardware supports
                         64 bit addresses for consistent
                         allocations such descriptors. */
    struct device_dma_parameters *dma_parms;
    struct list_head    dma_pools;  /* dma pools (if dma'ble) */
    struct dma_coherent_mem *dma_mem; /* internal for coherent mem
                         override */
    /* arch specific additions */
    struct dev_archdata archdata;
    struct device_node  *of_node; /* associated device tree node */
    dev_t           devt;   /* dev_t, creates the sysfs "dev" */
    spinlock_t      devres_lock;
    struct list_head    devres_head;
    struct klist_node   knode_class;
    struct class        *class;
    const struct attribute_group **groups;  /* optional groups */
    void    (*release)(struct device *dev);
;

Device_Driver:设备驱动程序,表示该设备的使用方法,设备模型中用struct device_driver表示,对应目录/sys/bus/pci/drivers/。
struct device_driver 
    const char      *name;
    struct bus_type     *bus;
    struct module       *owner;
    const char      *mod_name;  /* used for built-in modules */
    bool suppress_bind_attrs;   /* disables bind/unbind via sysfs */
    const struct of_device_id   *of_match_table;
    int (*probe) (struct device *dev);
    int (*remove) (struct device *dev);
    void (*shutdown) (struct device *dev);
    int (*suspend) (struct device *dev, pm_message_t state);
    int (*resume) (struct device *dev);
    const struct attribute_group **groups;
    const struct dev_pm_ops *pm; 
    struct driver_private *p;
;

Bus:总线,处理器与一个或多个设备间通信的通道,所有设备都是通过总线相连的,设备模型中用数据结构struct bus表示,对应目录/sys/bus/*/,常用的platform设备,spi设备,i2c设备的总线都是基于bus实现的。
struct bus_type 
    const char      *name;
    struct bus_attribute    *bus_attrs;
    struct device_attribute *dev_attrs;
    struct driver_attribute *drv_attrs;
    int (*match)(struct device *dev, struct device_driver *drv);
    int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
    int (*probe)(struct device *dev);
    int (*remove)(struct device *dev);
    void (*shutdown)(struct device *dev);
    int (*suspend)(struct device *dev, pm_message_t state);
    int (*resume)(struct device *dev);
    const struct dev_pm_ops *pm;
    struct iommu_ops *iommu_ops;    
    struct subsys_private *p;
;

Class:类,对所有已存在的设备进行分类,是上层对底层设备类型的抽象,设备模型中用数据结构struct class表示,对应目录/sys/class/。
struct class 
    const char      *name;
    struct module       *owner;
    struct class_attribute      *class_attrs;
    struct device_attribute     *dev_attrs;
    struct bin_attribute        *dev_bin_attrs;
    struct kobject          *dev_kobj;
    int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
    char *(*devnode)(struct device *dev, mode_t *mode);
    void (*class_release)(struct class *class);
    void (*dev_release)(struct device *dev);
    int (*suspend)(struct device *dev, pm_message_t state);
    int (*resume)(struct device *dev);
    const struct kobj_ns_type_operations *ns_type;
    const void *(*namespace)(struct device *dev);
    const struct dev_pm_ops *pm;
    struct subsys_private *p;
;

2.2 设备模型核心结构kobject、kset

由上面的4个结构可以看到均包含kobject,可以看做以上各个结构是对Kobject的继承。Kobject是linux设备模型中最基本的对象,他的作用是实现引用计数、父子结构和平级目录关系。

struct kobject 
    const char      *name;
    struct list_head    entry;
    struct kobject      *parent;//指向父节点
    struct kset     *kset;
    struct kobj_type    *ktype;
    struct sysfs_dirent *sd;//用于表示/sys系统中的每个目录项
    struct kref     kref; //其内部的refcount元素用于引用计数,见下文的struct kref
    unsigned int state_initialized:1;
    unsigned int state_in_sysfs:1;
    unsigned int state_add_uevent_sent:1;
    unsigned int state_remove_uevent_sent:1;
    unsigned int uevent_suppress:1;
;

struct kref 
    atomic_t refcount;
;
sysfs_dirent结构如下(kernel 3.0之后用kernfs_node):
struct sysfs_dirent 
        atomic_t                s_count;
        atomic_t                s_active;
        struct sysfs_dirent     *s_parent;
        struct sysfs_dirent     *s_sibling;
        const char              *s_name;
        union 
                struct sysfs_elem_dir           s_dir;
                struct sysfs_elem_symlink       s_symlink;
                struct sysfs_elem_attr          s_attr;
                struct sysfs_elem_bin_attr      s_bin_attr;
        ;
        unsigned int            s_flags;
        ino_t                   s_ino;
        umode_t                 s_mode;
        struct iattr            *s_iattr;
;
struct kernfs_node 
    atomic_t        count;
    atomic_t        active;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
    struct lockdep_map  dep_map;
#endif
    /*
     * Use kernfs_get_parent() and kernfs_name/path() instead of
     * accessing the following two fields directly.  If the node is
     * never moved to a different parent, it is safe to access the
     * parent directly.
     */
    struct kernfs_node  *parent;
    const char      *name;
    struct rb_node      rb;
    const void      *ns;    /* namespace tag */
    unsigned int        hash;   /* ns + name hash */
    union 
        struct kernfs_elem_dir      dir;
        struct kernfs_elem_symlink  symlink;
        struct kernfs_elem_attr     attr;
    ;
    void            *priv;
    unsigned short      flags;
    umode_t         mode;
    unsigned int        ino;
    struct kernfs_iattrs    *iattr;
;

kset可以理解为Kobject的集合,将同类型对象转入其中;其成员list 用于将集合中的 kobject 按entry 维护成双向链表。

struct kset 
    struct list_head list;
    spinlock_t list_lock;
    struct kobject kobj;
    const struct kset_uevent_ops *uevent_ops;
;

3 sys文件系统

上面几个概念的介绍,均可以理解为sys文件系统的组成元素,sysfs 是 Linux 内核中设计较新的一种虚拟的基于内存的文件系统,它的作用与 proc 有些类似,但除了与 proc 相同的具有查看和设定内核参数功能之外,还有为 Linux 统一设备模型作为管理之用。相比于 proc 文件系统,使用 sysfs 导出内核数据的方式更为统一,并且组织的方式更好,它的设计从 proc 中吸取了很多教训。

3.1 sys的目录结构

  • /sys/block 系统中所有块设备的集合
  • /sys/bus 系统中的总线集合
  • /sys/class 系统中的类集合
  • /sys/dev 系统中的字符设备和块设备的主次设备号集合
  • /sys/devices 系统中设备文件的集合
  • /sys/firmware 系统中固件的固件集合
  • /sys/fs 系统中所有文件系统的集合
  • /sys/hypervisor 虚拟机相关信息
  • /sys/kernel 系统中内核所有可调整参数的集合
  • /sys/module 系统中所有模块信息的集合
  • /sys/power 系统中所有电源管理选项集合

3.2 sys与Kobject/kset的联系

1.sys的顶层目录包含多个kset;
2.各个kset下又包含多个Kobject;
3.各个Kobject下包含多个属性和属性组;
4.各个Kobject还包含相应的链接文件,他们指向其他的Kobject(这就是bus、class、devices、driver相互之间的联系的原理);

3.3 DRIVER_ATTR,BUS_ATTR,CLASS_ATTR,DEVICE_ATTR

CLASS_ATTR,DRIVER_ATTR,BUS_ATTR,DEVICE_ATTR是快捷申明sys相关属性的系列宏:

CLASS_ATTR相关定义:
include/linux/device.h:
struct class_attribute 
    struct attribute attr;
    ssize_t (*show)(struct class *class, struct class_attribute *attr,
            char *buf);
    ssize_t (*store)(struct class *class, struct class_attribute *attr,
            const char *buf, size_t count);
    const void *(*namespace)(struct class *class,
                 const struct class_attribute *attr);               
;
#define CLASS_ATTR(_name, _mode, _show, _store)
struct class_attribute class_attr_##_name = __ATTR(_name, _mode, _show, _store)

include/linux/sysfs.h:
#define __ATTR(_name,_mode,_show,_store)  \\
    .attr = .name = __stringify(_name), .mode = _mode ,   \\
    .show   = _show,                    \\
    .store  = _store,                   \\


CLASS_ATTR使用流程见测试源码。
DRIVER_ATTR,BUS_ATTR,DEVICE_ATTR均和CLASS_ATTR类似。

4 测试源码

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/vmalloc.h>
#include <linux/device.h>
#include <linux/jiffies.h>// for jiffies
#include <linux/kernel.h>
#define MAX_COOKIE_SIZE PAGE_SIZE


/* class start */
static struct class sys_class=
    .name = "sys_class",
;
static ssize_t sys_class_show(struct class *class, struct class_attribute *attr,char *buf)

    printk("sys_class_show!\\n");
    snprintf(buf,PAGE_SIZE,"sys_class_show!\\n");
    return strlen(buf);


static ssize_t sys_class_store(struct class *class, struct class_attribute *attr,const char *buf, size_t count)

        printk("%s\\n",buf);
        return count;

static CLASS_ATTR(sys_class,S_IRUGO|S_IWUSR,sys_class_show,sys_class_store);
/* class end */


/* bus type start */
int ldd_match(struct device *dev, struct device_driver *drv)

    return !strcmp(dev->init_name,drv->name);

int ldd_uevent(struct device *dev, struct kobj_uevent_env *env)

    if(add_uevent_var(env,"LDD-VERSION:%s\\n","V1.0"))
        return -ENOMEM;

    return 0;


struct bus_type ldd_bus_type=
    .name = "ldd",
    .match = ldd_match,
    .uevent = ldd_uevent,
;

static ssize_t ldd_bus_show(struct bus_type *bus,char *buf)

    printk("ldd_bus_type show!\\n");
   return sprintf(buf,"ldd_bus_type show!\\n");

static BUS_ATTR(ldd_bus,S_IRUGO,ldd_bus_show,NULL);

/* bus type end */


/* device start */
static void ldd_bus_release(struct device *dev)

    printk("ldd_bus_release!\\n");


struct device ldd_bus=
    .init_name="ldd0",
    .release = ldd_bus_release,
    .class = &sys_class,
;

static ssize_t sys_show(struct device *dev, struct device_attribute *attr,char *buf)

    printk("sys_show!\\n");
    snprintf(buf,PAGE_SIZE,"sys_show!\\n");
    return strlen(buf);

static ssize_t sys_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count)

        printk("%s\\n",buf);
        return count;

static DEVICE_ATTR(sys_node, S_IRUGO | S_IWUSR, sys_show, sys_store);
/* device end */

/* driver start */
static struct device_driver sys_driver=
    .name = "sys_driver",
    .bus = &ldd_bus_type,
;

static ssize_t sys_driver_show(struct device_driver *driver, char *buf)

    printk("sys_driver_show!\\n");
    snprintf(buf,PAGE_SIZE,"sys_driver_show!\\n");
    return strlen(buf);


static ssize_t sys_driver_store(struct device_driver *driver, const char *buf,size_t count)

   printk("sys_driver_show!\\n");
    snprintf(buf,PAGE_SIZE,"sys_driver_show!\\n");
    return strlen(buf);


static ssize_t sys_driver_store(struct device_driver *driver, const char *buf,size_t count)

        printk("%s\\n",buf);
        return count;


static DRIVER_ATTR(sys_driver,S_IRUGO|S_IWUSR,sys_driver_show,sys_driver_store);
/* driver end */



static int __init sys_init(void)

    int ret=0;
    ret = class_register(&sys_class);
    if(ret)
        printk("Unable to register sys_class!\\n");
    ret = class_create_file(&sys_class,&class_attr_sys_class);

    ret = bus_register(&ldd_bus_type);
    if(ret)
        printk("Unable to register ldd_bus_type!\\n");
    ret = bus_create_file(&ldd_bus_type,&bus_attr_ldd_bus);

    ret = device_register(&ldd_bus);
    if(ret)
        printk("Unable to register ldd_bus!\\n");
    ret = device_create_file(&ldd_bus,&dev_attr_sys_node);
    ret = driver_register(&sys_driver);
    if(ret)
        printk("Unable to register sys_driver!\\n");
    ret = driver_create_file(&sys_driver,&driver_attr_sys_driver);
    printk(KERN_ERR "HELLO INIT!\\n");
    return ret;


static void __exit sys_exit(void)

    printk(KERN_ERR "HELLO EXIT!111111111111111111\\n");
    //device_remove_file(&ldd_bus,&dev_attr_sys_node);
    driver_unregister(&sys_driver);
    printk(KERN_ERR "HELLO EXIT!222222222222222222\\n");
    device_unregister(&ldd_bus);
    printk(KERN_ERR "HELLO EXIT!333333333333333333\\n");
    bus_unregister(&ldd_bus_type);
    class_unregister(&sys_class);

module_init(sys_init);
module_exit(sys_exit);

MODULE_LICENSE("Dual BSD/GPL");

以上是关于linux设备模型与sys文件系统的主要内容,如果未能解决你的问题,请参考以下文章

主次设备号的应用

[架构之路-35]:目标系统 - 系统软件 - Linux OS内核模块与内核设备驱动程序,一切皆文件,Linux虚拟文件系统与统一设备模型

Linux 设备模型之 (kobjectkset 和 Subsystem)

linux设备驱动(11) dev sys/dev sys/devices的区别

主次设备号 Device Major and Minor Numbers

字符设备驱动详解(主次设备号注册/卸载字符设备驱动创建设备节点地址映射)