《linux 内核设备管理模型sysfs(入门篇)》讲述了内核设备管理模型sysfs的基础管理单元kobject和kset,但是在实际过程中很少有驱动工程师有机会直接操作上述结构,linux在上述结构的基础上又进一步进行了高级抽象:总线bus、驱动driver、设备device,这三个重要的抽象。

一般在处理器中 要与外设进行打交到就要通过各自总线与外界设备链接起来,经常用到的总结比如pci、usb、I2c等结构,各自总线被按照逻辑抽象到bus结构中,在sys/bus中可以看到各种常用的总线:

就好比一个usb鼠标,通过usb总线与CPU进行链接,与抽象到的bus类型为usb进行打交到,但是只有bus驱动是不够的,因为每个总线上可以接各种不同类型的设备,比如usb上即可以链接usb 鼠标还可以链接键盘、U盘等,每种类型的设备都需要各种的驱动,所以linux将相同类型的设备驱动抽象成driver,比如鼠标就有属于自己专门的driver,键盘有自己的专门driver。

如下图所示 为笔者usb总线driver下面的driver:

但是bus总线和driver驱动还是不不够,因为一个总线上有可能接多个相同的设备,比如一台电脑接两个鼠标或者键盘,那么每个鼠标都需要有一个自己的device抽象,用来与另外一个鼠标进行区别,这个称之为device, device相当于书driver的实例化,两个鼠标分别被实例化为mouse1 和mouse 2两个device,以达到两个鼠标互不干扰的目的。

下图为笔者 usb device设备,每个设备都是通过软件来链接到device目录,和/sys/devices内的目录进行链接,实际上/sys/devices为实际上所有设备所出的目录位置。

总线BUS

linux设备管理模型在kset更高一个层级,将每个总线抽象成bus_type结构,在linux 5.4.56版本中,其主要结构如下(定义位于include\linux\device.h文件中):

struct bus_type {const char      *name;const char        *dev_name;struct device     *dev_root;const struct attribute_group **bus_groups;const struct attribute_group **dev_groups;const struct attribute_group **drv_groups;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 (*online)(struct device *dev);int (*offline)(struct device *dev);int (*suspend)(struct device *dev, pm_message_t state);int (*resume)(struct device *dev);int (*num_vf)(struct device *dev);int (*dma_configure)(struct device *dev);const struct dev_pm_ops *pm;const struct iommu_ops *iommu_ops;struct subsys_private *p;struct lock_class_key lock_key;bool need_parent_lock;
};
  • bus_type结构是所有位于sys/bus一级目录的结构抽象,整个bus实现位于drivers\base\bus.c文件中,几个比较重要的-结构:
  • name: 该bus名称,比如usb等
  • dev_name: 用于在该子系统内的device name,和devic id结合在一起用于命名该bus中多个device .
  • dev_root: 被用作默认device的parent
  • bus_groups:默认bus属性。
  • dev_groups: 在该bus上的device默认属性。
  • drv_groups: 在该bus上的driver默认属性
  • match:bus中发现一个新的device是,调用match 是否与某个device或者driver匹配
  • uevent: 添加删除等时间
  • probe: 当一个新的device或者driver加入到bus中,用于探测该设备是否存在
  • remove: 移除设备处理
  • shutdwn: 关闭 一个device
  • online:  设备上线时会调用该回调
  • offline: 设备下线
  • suspend: 设备被挂起 处于sleep模式
  • resume: 从sleep中进行唤醒
  • struct subsys_private *p:每个bus的私有数据,里面包含了该bus下所有的device和driver, 主要数据如下:
struct subsys_private {struct kset subsys;struct kset *devices_kset;struct list_head interfaces;struct mutex mutex;struct kset *drivers_kset;struct klist klist_devices;struct klist klist_drivers;struct blocking_notifier_head bus_notifier;unsigned int drivers_autoprobe:1;struct bus_type *bus;struct kset glue_dirs;struct class *class;
};
  • struct kset subsys:为该bus下所有的总线的集合,
  • struct klist klist_devices: 该bus下所有device集合通过链表串联起来
  • struct klist klist_drivers:该bus下所有driver集合
  • struct bus_type:类似与kobjece中的kobj_type,相同类型bus。

BUS API

linux kernel bus模块提供了一些基本的接口供驱动开发人员使用:

API 作用及说明
nt bus_register(struct bus_type *bus) 注册一个bus
void bus_unregister(struct bus_type *bus) 去注册一个bus
int bus_create_file(struct bus_type *bus, struct bus_attribute *attr) 为该bus 创建一个bus属性
void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr) 为该bus中移除掉一个bus属性
struct klist *bus_get_device_klist(struct bus_type *bus) 获取到该bus ket
struct klist *bus_get_device_klist(struct bus_type *bus) 获取该bus中所有的device
struct device *bus_find_device(struct bus_type *bus,   struct device *start, const void *data,     int (*match)(struct device *dev, const void *data)) 查找该bus中某个device
int bus_register_notifier(struct bus_type *bus, struct notifier_block *nb) 注册该bus中notdifer
int bus_unregister_notifier(struct bus_type *bus, struct notifier_block *nb) 去注册该bus中notdifer
int bus_rescan_devices(struct bus_type *bus) 重新对该bus中的设备进行扫描
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,  void *data, int (*fn)(struct device_driver *, void *)) 遍历该bus内的driver

创建Mybus用例

通过Mybus用例来更加了解bus的工作原理以及总线驱动的一些更深层次的理解,创建一个名为虚拟的Mybus 总线,其创建的总线与usb、pci等总线并行,调用bus_register进行注册,用例示范代码如下:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/device.h>
MODULE_LICENSE("GPL v2");static int my_match(struct device * dev, struct device_driver * driver)
{printk(KERN_INFO "my bus match dev name %s, driver name %s\n",dev_name(dev), driver->name);return !strncmp(dev_name(dev),driver->name, strlen(driver->name));
}static int my_uevent(struct device *dev, struct kobj_uevent_env *env)
{printk(KERN_INFO "my bus uevent dev name %s\n",dev_name(dev));add_uevent_var(env, "DEV_NAME=%s", dev_name(dev));return 0;
}static struct bus_type my_bus_type= {.name   = "mybus",.match  = my_match,.uevent = my_uevent,
};static int __init bus_test_init(void)
{int err;err = bus_register(&my_bus_type);if (err){printk(KERN_INFO "bus test init failed\n");return err;}return 0;
}static void __exit bus_test_exit(void)
{bus_unregister(&my_bus_type);
}module_init(bus_test_init);
module_exit(bus_test_exit);
MODULE_AUTHOR("hzk");
MODULE_DESCRIPTION("A simple moudule for bus test");
MODULE_VERSION("V1.0");

用例中创建一个static struct bus_type结构my_bus_type,然后调用bus_register进行注册,创建一个新的总线名为mybus,运行结果如下:

可以看到如上图所示创建一个名为mybus的总线与其他总线并列,进一步查看mybus目录,发现默认会创建如下结构:

其中devices和drivers类型分别为kset,对应的是struct subsys_private *p结构中的struct klist klist_devices和 struct klist klist_drivers,可以分别查看该总线上的所有的device和drivers。

bus_attribute

bus_attribute是基于attribute的一个简单封装,提供show和store 回调, 类似一与kobj_attribute结构,定义如下:

struct bus_attribute {struct attribute   attr;ssize_t (*show)(struct bus_type *bus, char *buf);ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);
};

创建bus的属性有两种方法 一种是通过bus_type在创建是指定bus_groups 创建默认attribute,另外一种是通过bus_create_file创建非默认。

默认bus_attribute创建用例

使用默认attribute创建mybus 时 指定两个属性分别为para1和para2 , 用例代码如下:

#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/device.h>
MODULE_LICENSE("GPL v2");static int my_match(struct device * dev, struct device_driver * driver)
{printk(KERN_INFO "my bus match dev name %s, driver name %s\n",dev_name(dev), driver->name);return !strncmp(dev_name(dev),driver->name, strlen(driver->name));
}static int my_uevent(struct device *dev, struct kobj_uevent_env *env)
{printk(KERN_INFO "my bus uevent dev name %s\n",dev_name(dev));add_uevent_var(env, "DEV_NAME=%s", dev_name(dev));return 0;
}static ssize_t para1_show(struct bus_type *bus, char *buf)
{return snprintf(buf, PAGE_SIZE, "para1 test\n");
}BUS_ATTR_RO(para1);
static ssize_t para2_show(struct bus_type *bus, char *buf)
{return snprintf(buf, PAGE_SIZE, "para1 test\n");
}BUS_ATTR_RO(para2);static struct attribute *mybus_para_attrs[]={&bus_attr_para1.attr,&bus_attr_para2.attr,NULL,
};
ATTRIBUTE_GROUPS(mybus_para);static struct bus_type my_bus_type= {.name   = "mybus",.match  = my_match,.uevent = my_uevent,.bus_groups = mybus_para_groups
};
static int __init bus_test_init(void)
{int err;err =  bus_register(&my_bus_type);if (err){printk(KERN_INFO "bus register failed\n");return err;}return 0;
}static void __exit bus_test_exit(void)
{bus_unregister(&my_bus_type);
}module_init(bus_test_init);
module_exit(bus_test_exit);
MODULE_AUTHOR("hzk");
MODULE_DESCRIPTION("A simple moudule for bus test");
MODULE_VERSION("V1.0");

运行结果如下:

创建非默认bus_attribute

使用bus_create_file()创建mybus非默认bus_attribute 名字分别为descr1和descr2,代码示例如下:

include <linux/init.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/device.h>
MODULE_LICENSE("GPL v2");static int my_match(struct device * dev, struct device_driver * driver)
{printk(KERN_INFO "my bus match dev name %s, driver name %s\n",dev_name(dev), driver->name);return !strncmp(dev_name(dev),driver->name, strlen(driver->name));
}static int my_uevent(struct device *dev, struct kobj_uevent_env *env)
{printk(KERN_INFO "my bus uevent dev name %s\n",dev_name(dev));add_uevent_var(env, "DEV_NAME=%s", dev_name(dev));return 0;
}static struct bus_type my_bus_type= {.name   = "mybus",.match  = my_match,.uevent = my_uevent,
};static ssize_t descr1_show(struct bus_type *bus, char *buf)
{return snprintf(buf, PAGE_SIZE, "mybus descr 1\n");
}BUS_ATTR_RO(descr1);
static ssize_t descr2_show(struct bus_type *bus, char *buf)
{return snprintf(buf, PAGE_SIZE, "mybus descr 2\n");
}BUS_ATTR_RO(descr2);
static int __init bus_test_init(void)
{int err;err =  bus_register(&my_bus_type);if (err){printk(KERN_INFO "bus register failed\n");return err;}err = bus_create_file(&my_bus_type, &bus_attr_descr1);if (err){printk(KERN_INFO "bus creat descr1 failed\n");return err;}err = bus_create_file(&my_bus_type, &bus_attr_descr2);if (err){printk(KERN_INFO "bus creat descr2 failed\n");return err;}return 0;
}static void __exit bus_test_exit(void)
{bus_remove_file(&my_bus_type, &bus_attr_descr1);bus_remove_file(&my_bus_type, &bus_attr_descr2);bus_unregister(&my_bus_type);
}
module_init(bus_test_init);
module_exit(bus_test_exit);
MODULE_AUTHOR("hzk");
MODULE_DESCRIPTION("A simple moudule for bus test");
MODULE_VERSION("V1.0");

运行结果如下:

驱动driver

从一个CPU角度来将,CPU与各种外设都是通过各种总线进行打交道,一个设备可能使用到多种总线类型,仅仅用于bus结构还不足以支撑整个系统运转,在一个总线上会有各种各样的设备,就需要不同类型的驱动,对于linux 内核驱动而言,一个设备的驱动一定会归属于某个总线,没有一个驱动是没有相关总线的,内核将总线上的驱动抽象为device_driver 结构,在5.4.56内核版本种该结构如下:

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 */enum probe_type probe_type;const struct of_device_id    *of_match_table;const struct acpi_device_id *acpi_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 attribute_group **dev_groups;const struct dev_pm_ops *pm;void (*coredump) (struct device *dev);struct driver_private *p;
};
  • name: driver name
  • bus: 该驱动归属的bus总线
  • owner:驱动所属的module.
  • mod_name: 用于在 built-in  module模块名
  • suppress_bind_attrs: 是否需要通过sysfs文件系统进行绑定
  • probe: 用于探测设备是否存在。
  • remove:设备移除
  • shutdown:关闭 设备
  • suspend:挂起设备使之处于sleep状态
  • resume: 从sleep状态种唤醒
  • groups:默认属性
  • dev_groups:一旦一个设备实例化  将会将该属性绑定到该设备种
  • struct driver_private *p:私有数据,数据类型如下:
struct driver_private {struct kobject kobj;struct klist klist_devices;struct klist_node knode_bus;struct module_kobject *mkobj;struct device_driver *driver;
};
  • struct kobject kobj: 因为同一种设备类型只有一个驱动即可 故device_driver数据结构为基于kobject进行抽象即可
  • struct klist klist_devices:该驱动有多少个具体的device实例化
  • struct device_driver *driver: 该私有数据对应的para device_driver,主要是为了根据kobj 通过contain_of查找到driver_private,然后查找到所属的device_driver结构

device_driver API

在内核驱动模块种drivers\base\driver.c提供了对驱动使用的API,主要列表如下:

API 作用及说明
int driver_register(struct device_driver *drv) 注册一个driver
void driver_unregister(struct device_driver *drv) 去注册一个driver
int driver_create_file(struct device_driver *drv,    const struct driver_attribute *attr) 为该driver创建属性文件
void driver_remove_file(struct device_driver *drv,     const struct driver_attribute *attr) 删除该driver属性文件
struct device *driver_find_device(struct device_driver *drv,   struct device *start, const void *data,   int (*match)(struct device *dev, const void *data)) 在该driver内查找某个device
int driver_for_each_device(struct device_driver *drv, struct device *start,    void *data, int (*fn)(struct device *, void *)) 遍历该driver内的所有device
struct device_driver *driver_find(const char *name, struct bus_type *bus) 在bus内根据name 查找driver

device_driver用例

使用driver相关接口 在my_bus上注册一个mydriver驱动,用例代码如下:

include <linux/init.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/device.h>
MODULE_LICENSE("GPL v2");static int my_match(struct device * dev, struct device_driver * driver)
{printk(KERN_INFO "my bus match dev name %s, driver name %s\n",dev_name(dev), driver->name);return !strncmp(dev_name(dev),driver->name, strlen(driver->name));
}static int my_uevent(struct device *dev, struct kobj_uevent_env *env)
{printk(KERN_INFO "my bus uevent dev name %s\n",dev_name(dev));add_uevent_var(env, "DEV_NAME=%s", dev_name(dev));return 0;
}static struct bus_type my_bus_type= {.name   = "mybus",.match  = my_match,.uevent = my_uevent,
};static struct device_driver my_driver ={.name ="my_driver",.bus  = &my_bus_type,
};static int __init driver_test_init(void)
{int err;err = bus_register(&my_bus_type);if (err){printk(KERN_INFO "bus test init failed\n");return err;}err =  driver_register(&my_driver);if (err){printk(KERN_INFO "my driver register failed\n");return err;}return 0;
}static void __exit device_test_exit(void)
{driver_unregister(&my_driver);bus_unregister(&my_bus_type);
}module_init(driver_test_init);
module_exit(device_test_exit);
MODULE_AUTHOR("hzk");
MODULE_DESCRIPTION("A simple moudule for device test");
MODULE_VERSION("V1.0");

代码中my_driver一定要指明所在的bus,device_driver结构在sys中没有专门的目录,只能在所属的bus中查看,如果是一个单独的ko则可以在/sys/module中进行查看:

设备Deivce

device是一个驱动driver的实例化,指向具体的一个设备,是一个基于kobject的struct device结构,主要结构如下:

struct device {struct kobject kobj;struct device     *parent;struct device_private   *p;const char       *init_name; /* initial name of the device */const struct device_type *type;struct bus_type  *bus;       /* type of bus device is on */struct device_driver *driver; /* which driver has allocated thisdevice */void     *platform_data; /* Platform specific data, devicecore doesn't touch it */void      *driver_data;   /* Driver data, set and get withdev_set_drvdata/dev_get_drvdata */
#ifdef CONFIG_PROVE_LOCKINGstruct mutex     lockdep_mutex;
#endifstruct mutex      mutex;  /* mutex to synchronize calls to* its driver.*/struct dev_links_info    links;struct dev_pm_info    power;struct dev_pm_domain  *pm_domain;#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAINstruct irq_domain    *msi_domain;
#endif
#ifdef CONFIG_PINCTRLstruct dev_pin_info    *pins;
#endif
#ifdef CONFIG_GENERIC_MSI_IRQstruct list_head   msi_list;
#endifconst struct dma_map_ops *dma_ops;u64     *dma_mask;  /* dma mask (if dma'able device) */u64     coherent_dma_mask;/* Like dma_mask, but foralloc_coherent mappings asnot all hardware supports64 bit addresses for consistentallocations such descriptors. */u64        bus_dma_mask;   /* upstream dma_mask constraint */unsigned long dma_pfn_offset;struct device_dma_parameters *dma_parms;struct list_head dma_pools;  /* dma pools (if dma'ble) */#ifdef CONFIG_DMA_DECLARE_COHERENTstruct dma_coherent_mem  *dma_mem; /* internal for coherent memoverride */
#endif
#ifdef CONFIG_DMA_CMAstruct cma *cma_area;      /* contiguous memory area for dmaallocations */
#endif/* arch specific additions */struct dev_archdata  archdata;struct device_node *of_node; /* associated device tree node */struct fwnode_handle *fwnode; /* firmware device node */#ifdef CONFIG_NUMAint        numa_node;  /* NUMA node this device is close to */
#endifdev_t         devt;   /* dev_t, creates the sysfs "dev" */u32           id; /* device instance */spinlock_t     devres_lock;struct list_head    devres_head;struct class        *class;const struct attribute_group **groups;   /* optional groups */void   (*release)(struct device *dev);struct iommu_group   *iommu_group;struct iommu_fwspec    *iommu_fwspec;struct iommu_param    *iommu_param;bool           offline_disabled:1;bool         offline:1;bool          of_node_reused:1;
#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL)bool            dma_coherent:1;
#endif
};

主要结构成员:

  • struct kobject kobj:内嵌的kobject结构。
  • struct device    *parent:指向父设备,有时为 bus or host   controllor控制器,如果为NULL则为处于top设备。
  • const char        *init_name:该设备名称。
  • struct bus_type    *bus:该设备所属于的bus总线
  • struct device_private    *p:该设备的私有数据成员,主要数据内容如下:
struct device_private {struct klist klist_children;struct klist_node knode_parent;struct klist_node knode_driver;struct klist_node knode_bus;struct klist_node knode_class;struct list_head deferred_probe;struct device_driver *async_driver;struct device *device;u8 dead:1;
};
  • knode_bus:所属bus。
  • knode_class:所属设备类型。
  • struct klist klist_children:如果该设备有子设备 ,则含有所有的子设备。
  • struct klist_node knode_parent:所属的父设备。

device API

device主要实现位于drivers\base\core.c中:

API 主要作用说明
int device_register(struct device *dev) 注册一个设备
void device_unregister(struct device *dev) 去注册一个设备
void device_initialize(struct device *dev) 初始化一个设备
int device_add(struct device *dev) 添加一个设备
void device_del(struct device *dev) 删除一个设备
int device_add_groups(struct device *dev, const struct attribute_group **groups) 添加设备属性组
void device_remove_groups(struct device *dev,      const struct attribute_group **groups) 移除一个设备组
struct device *device_create_with_groups(struct class *class,
                     struct device *parent, dev_t devt,
                     void *drvdata,
                     const struct attribute_group **groups,
                     const char *fmt, ...)
创建一个设备 并使用groups初始化
void device_destroy(struct class *class, dev_t devt) destory一个设备
struct device *device_create_vargs(struct class *class, struct device *parent,
                   dev_t devt, void *drvdata, const char *fmt,
                   va_list args)
创建一个设备

创建MyDevice用例

使用上述API 创建一个Mydeice用例:

include <linux/init.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/device.h>
MODULE_LICENSE("GPL v2");static struct device my_device ={.init_name ="my_device",
};static int __init device_test_init(void)
{int err;err =  device_register(&my_device);if (err){printk(KERN_INFO "my device register failed\n");return err;}return 0;
}static void __exit device_test_exit(void)
{device_unregister(&my_device);
}module_init(device_test_init);
module_exit(device_test_exit);
MODULE_AUTHOR("hzk");
MODULE_DESCRIPTION("A simple moudule for device test");
MODULE_VERSION("V1.0");

创建所有的device都位于sys/devices目录

MyBus、MyDriver、MyDeivce

bus_type是CPU对外总线类型bus的抽象,CPU一个外设必然是通过各种总线打交道,一般而言总线级别的驱动实现基于bus_type,例如pci在此基础上进行了封装pci驱动头文件位于\include\linux\pci.h文件中该文件为pci对外提供的pci驱动接口,具体实现每个芯片略有差异。总线之外外挂各种类型的设备,需要在总线之上实现各种类型设备的驱动,一般会在 device_driver层进行封装实现。 device为各种类型设备的实例化相当于device_driver的实例化。

在上述用例中使用MyBus来模拟一个bus类型的总线,MyDriver为基于MyBus上的一个设备驱动,MyDevice为MyDriver驱动的一个具体的设备,这样可以更加理解linux驱动模型,整体结构如下:

对于linux 驱动模型 一般而言总线驱动实现需要提供register,attribute等接口以便当一个新的设备类型时,方便基于该总线进行新设备的driver驱动开发,创建mybus层 其数据结构为my_bus_type。在mybus上的驱动封装成struct my_driver,再次基础上实现mydriver驱动实现,myDevice基于device 进行了封装my_device。整个linux 驱动要实现是模型思路就是如此。

用例代码如下:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/device.h>
MODULE_LICENSE("GPL v2");static int my_match(struct device * dev, struct device_driver * driver)
{printk(KERN_INFO "my bus match dev name %s, driver name %s\n",dev_name(dev), driver->name);return !strncmp(dev_name(dev),driver->name, strlen(driver->name));
}static int my_uevent(struct device *dev, struct kobj_uevent_env *env)
{printk(KERN_INFO "my bus uevent dev name %s\n",dev_name(dev));add_uevent_var(env, "DEV_NAME=%s", dev_name(dev));return 0;
}static struct bus_type my_bus_type= {.name   = "mybus",.match  = my_match,.uevent = my_uevent,
};static struct device_driver my_driver ={.name ="my_driver",.bus  = &my_bus_type,
};static struct device my_device ={.init_name ="my_device",.bus       =&my_bus_type,.driver    =&my_driver};static int __init driver_test_init(void)
{int err;err = bus_register(&my_bus_type);if (err){printk(KERN_INFO "bus test init failed\n");return err;}err =  driver_register(&my_driver);if (err){printk(KERN_INFO "my driver register failed\n");return err;}err =  device_register(&my_device);if (err){printk(KERN_INFO "my device register failed\n");return err;}return 0;
}
static void __exit device_test_exit(void)
{device_unregister(&my_device);driver_unregister(&my_driver);bus_unregister(&my_bus_type);
}module_init(driver_test_init);
module_exit(device_test_exit);
MODULE_AUTHOR("hzk");
MODULE_DESCRIPTION("A simple moudule for device test");
MODULE_VERSION("V1.0");

运行模块记载完成之后,在sys/mybus的组织拓扑结构如下:

mydevice设备是一个通过虚拟的mybus总线结构打交道,故mybus/devices目录下软链接到my_device形式,同时mydevice是基于my_driver的实例化,故再my_driver目录下同样会软链接到device下。

总结

总线,驱动和设备是linux设备模型中比较关键的模型,虽然在如今大多数系统中不需要驱动工作者去真正创建一个总线,但是了解该模型能够更好理解linux中一个驱动如何工作的,不仅要知其然,还要知其所以然。以上主要是通过一个个具体实例来了解整个模型,如果还要具体了解可以学习《linux 设备驱动程序》这本书。

类class

类是设备模型中的最后一个概念,是基于device的高层抽象,将设备的底层实现细节进行抽象。通常而言对于SCSI磁盘或者ATA磁盘等设备而言,在类的层次而言都是磁盘而已,类将多种设备类型中相同的功能进行抽象,可以减少代码重复率,然后设备再基于此类进行实例化。如果说bus是总线的抽象,那么类就是设备的相同功能的抽象。类允许用户空间使用设备所提供的功能,而不关心设备是如何连接的以及它是如何工作的。

几乎所有的类都显示在/sys/class目录中。举例子说明,不管是网络接口的类型是什么,所有的网络接口都集中在/sys/class /net中。输入设备 都可以在/sys/class/input下找到。

设备驱动模型中将类都抽象成strut class结构:

struct class {const char     *name;struct module     *owner;const struct attribute_group **class_groups;const struct attribute_group **dev_groups;struct kobject         *dev_kobj;int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);char *(*devnode)(struct device *dev, umode_t *mode);void (*class_release)(struct class *class);void (*dev_release)(struct device *dev);int (*shutdown_pre)(struct device *dev);const struct kobj_ns_type_operations *ns_type;const void *(*namespace)(struct device *dev);void (*get_ownership)(struct device *dev, kuid_t *uid, kgid_t *gid);const struct dev_pm_ops *pm;struct subsys_private *p;
};
  • name:为该类名称
  • owner:为该类所属module
  • class_groups:为该类默认属性
  • dev_groups:为对应的设备属性
  • dev_kobj:代表该class的kobj
  • 一些回调:相应的回调动作
  • subsys_private *p:对应类类的私有结构,与bus_type一样。

class用例

类使用的一个简单用例:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/device.h>
MODULE_LICENSE("GPL v2");static struct class my_class = {.name = "myclass",
};static int __init class_test_init(void)
{int err;err = class_register(&my_class);if (err){printk(KERN_INFO "class test init failed\n");return err;}return 0;
}static void __exit class_test_exit(void)
{class_unregister(&my_class);
}module_init(class_test_init);
module_exit(class_test_exit);
MODULE_AUTHOR("hzk");
MODULE_DESCRIPTION("A simple moudule for class test");
MODULE_VERSION("V1.0");

运行结果/sys/class目录产生一个新类:

创建基于Myclass的device用例

创建一个基于Myclass的Mydevice用例:

include <linux/init.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/device.h>
MODULE_LICENSE("GPL v2");static struct class my_class = {.name = "myclass",
};static struct device my_device ={.init_name ="my_device",.class     =&my_class,
};static int __init class_test_init(void)
{int err;err = class_register(&my_class);if (err){printk(KERN_INFO "class test init failed\n");return err;}err =  device_register(&my_device);if (err){printk(KERN_INFO "my device register failed\n");return err;}return 0;
}static void __exit class_test_exit(void)
{device_unregister(&my_device);class_unregister(&my_class);
}module_init(class_test_init);
module_exit(class_test_exit);
MODULE_AUTHOR("hzk");
MODULE_DESCRIPTION("A simple moudule for class test");
MODULE_VERSION("V1.0");

运行结果如下:

linux 内核设备管理模型sysfs(进阶篇)相关推荐

  1. linux 内核设备管理模型sysfs(入门篇)

    sysfs是linux 内核从2.6中开始引入的设备管理模型,该模型通过一个虚拟的文件系统sysfs 将整个硬件的层级模型显现和组织起来,并将其采用矩阵式管理方式,还是支持热插拔等功能,该设备模型自从 ...

  2. linux 内核模型,The Linux Kernel Device Model - Overview -- Linux 内核设备模型概述

    --------------------------------------------------------------------------------------------------- ...

  3. 【Linux内核系列】百分百干货篇丨不要错过,5个方面分析内核架构,让你对内核不再陌生

    5个方面分析内核架构,让你对内核不再陌生 1. 内存管理 mm_struct 2. 进程管理 task_struct 3. 网络协议栈 sk_buff 4. 文件系统 super_block 5. 设 ...

  4. linux 内核驱动模型,linux设备驱动模型架构分析 一

    linux设备驱动模型架构分析 一 发布时间:2018-07-04 15:14, 浏览次数:584 , 标签: linux 概述 LDD3中说:"Linux内核需要一个对系统结构的一般性描述 ...

  5. 和菜鸟一起学linux内核源码之启动篇

    又是一个周末,日子过得比较散,虽然期间也有不断地看书学习,总觉得有点小盲目.想想毕业也快要1年了,从事嵌入式linux的研发工作也1年多了.这1年多的从实习到正式工作到现在的自己,进步有,也很大,但是 ...

  6. linux内核基本模型,Linux设备模型(1)_基本概念

    Linux设备模型(1)_基本概念 作者:wowo 发布于:2014-2-27 17:01 分类:统一设备模型 1. 前言 在"Linux内核的整体架构"中,蜗蜗有提到,由于Lin ...

  7. 学习linux内核-- 内存,看一篇就够了(多图)

    内存模块 1 linux内存总体布局:内存分成用户态和内核态 4G进程地址空间解析 内核地址空间 进程地址空间 2 地址转换和页表 2.1 地址转换 虚拟内存是指程序使用的逻辑地址.每个进程4G.所有 ...

  8. linux内核设备管理典型算法,linux内核物理存储空间管理有哪些常用算法

    Linux进程调度 kill命令 当需要中断一个前台进程的时候,通常是使用< Ctrl+c >组合键:但是对于一个后台进程恐怕就不是一个组合键所能解决的了,这时就必须求助于kill命令.该 ...

  9. Linux基础之bash脚本进阶篇-循环语句(for,while,until)

    20160909 补充break与continue的区别 什么是循环语句.死循环? 循环语句:将一段代码重复执行0.1或多次. 到底要重复运行多少次?以及我们如何设定循环语句的重复次数? 为了解决上面 ...

最新文章

  1. anago 围棋_跳进我的碗里—关于中惒围棋加盟那些事
  2. [Spark][Flume]Flume 启动例子
  3. Node.js用6行代码1个JS文件搭建一个HTTP静态服务器
  4. zend studio mysql 配置_php 在Zend Framework中配置数据库参数
  5. 在普通java类里获取Spring管理的bean
  6. android seekbar 圆角,android - 最小或最大时,圆角android seekbar的ui奇怪行为 - 堆栈内存溢出...
  7. Eclipse/NSight: methond could not resolved
  8. 使用iPhone系统设置开发者,进行弱网测试
  9. JAVA300集速学堂高淇个人笔记P1-P7如何学习JAVA300集计算机语言的发展历史多种编程语言的介绍JAVA三大版本的含义:
  10. win10html网页运行空白,win10系统Ie浏览器无法打开HTML格式的网页文件的处理秒方...
  11. Wannacry 勒索病毒有预设的解密口令“WNcry@2ol7”么?
  12. java http心跳_MQTT协议笔记之连接和心跳
  13. 正则表达式-注册表验证
  14. linux 静态编译多媒体框架,Go编译32位GNU静态链接库的方法
  15. MySQL Study之--Mysql数据库备份工具(mysqldump)
  16. <四>关于flv格式解析
  17. knewton适应性学习白皮书(2)
  18. c#读取CSV格式文件
  19. python画图小游戏课程设计
  20. N-Case 律师事务所管理系统功能介绍

热门文章

  1. P3-weixin-2.0.0版本发布(微信插件式开发框架)
  2. 运行时异常与一般异常有何异同?
  3. 利用Xcode在Mac写第一个C++ project
  4. Redis解读持久化RDB和AOF原理
  5. 对Url Schemes的简单了解
  6. 从把事做对到做对的事
  7. 关于centos 7 中service iptables save 指令使用失败的结局方案
  8. spring mvc controller json数据
  9. 防御CSRF、XSS和SQL注入***
  10. 使用Ext JS,不要使用页面做组件重用,尽量不要做页面跳转