Linux设备模型(5)_device和device driver

作者:wowo 发布于:2014-4-2 19:28 分类:统一设备模型

1. 前言

device和device driver是Linux驱动开发的基本概念。Linux kernel的思路很简单:驱动开发,就是要开发指定的软件(driver)以驱动指定的设备,所以kernel就为设备和驱动它的driver定义了两个数据结构,分别是device和device_driver。因此本文将会围绕这两个数据结构,介绍Linux设备模型的核心逻辑,包括:

设备及设备驱动在kernel中的抽象、使用和维护;

设备及设备驱动的注册、加载、初始化原理;

设备模型在实际驱动开发过程中的使用方法。

注:在介绍device和device_driver的过程中,会遇到很多额外的知识点,如Class、Bus、DMA、电源管理等等,这些知识点都很复杂,任何一个都可以作为一个单独的专题区阐述,因此本文不会深入解析它们,而会在后续的文章中专门描述。

2. struct device和struct device_driver

在阅读Linux内核源代码时,通过核心数据结构,即可理解某个模块60%以上的逻辑,设备模型部分尤为明显。

在include/linux/device.h中,Linux内核定义了设备模型中最重要的两个数据结构,struct device和struct device_driver。

  • struct device
 1: /* include/linux/device.h, line 660 */
 2: struct device {
 3:     struct device       *parent;
 4:  
 5:     struct device_private   *p;
 6:  
 7:     struct kobject kobj;
 8:     const char *init_name; /* initial name of the device */
 9:     const struct device_type *type;
 10:  
 11:    struct mutex        mutex; /* mutex to synchronize calls to
 12:                             * its driver.
 13:                             */
 14:  
 15:    struct bus_type *bus; /* type of bus device is on */
 16:    struct device_driver *driver; /* which driver has allocated this
 17:                                 device */
 18:    void *platform_data; /* Platform specific data, device
 19:                         core doesn't touch it */
 20:    struct dev_pm_info  power;
 21:    struct dev_pm_domain    *pm_domain;
 22:  
 23: #ifdef CONFIG_PINCTRL
 24:    struct dev_pin_info *pins;
 25: #endif
 26:  
 27: #ifdef CONFIG_NUMA
 28:    int numa_node; /* NUMA node this device is close to */
 29: #endif
 30:    u64     *dma_mask; /* dma mask (if dma'able device) */
 31:    u64     coherent_dma_mask;/* Like dma_mask, but for
 32:                             alloc_coherent mappings as
 33:                             not all hardware supports
 34:                             64 bit addresses for consistent
 35:                             allocations such descriptors. */
 36:  
 37:    struct device_dma_parameters *dma_parms;
 38:  
 39:    struct list_head    dma_pools; /* dma pools (if dma'ble) */
 40:  
 41:    struct dma_coherent_mem *dma_mem; /* internal for coherent mem
 42:                            override */
 43: #ifdef CONFIG_CMA
 44:    struct cma *cma_area; /* contiguous memory area for dma
 45:                            allocations */
 46: #endif
 47:    /* arch specific additions */
 48:    struct dev_archdata archdata;
 49:  
 50:    struct device_node  *of_node; /* associated device tree node */
 51:    struct acpi_dev_node    acpi_node; /* associated ACPI device node */
 52:  
 53:    dev_t           devt; /* dev_t, creates the sysfs "dev" */
 54:    u32         id; /* device instance */
 55:  
 56:    spinlock_t      devres_lock;
 57:    struct list_head    devres_head;
 58:  
 59:    struct klist_node   knode_class;
 60:    struct class *class;
 61:    const struct attribute_group **groups; /* optional groups */
 62:  
 63:    void (*release)(struct device *dev);
 64:    struct iommu_group  *iommu_group;
 65: };

device结构很复杂(不过linux内核的开发人员素质是很高的,该接口的注释写的非常详细,感兴趣的同学可以参考内核源代码),这里将会选一些对理解设备模型非常关键的字段进行说明。

parent,该设备的父设备,一般是该设备所从属的bus、controller等设备。

p,一个用于struct device的私有数据结构指针,该指针中会保存子设备链表、用于添加到bus/driver/prent等设备中的链表头等等,具体可查看源代码。

kobj,该数据结构对应的struct kobject。

init_name,该设备的名称。

       注1:在设备模型中,名称是一个非常重要的变量,任何注册到内核中的设备,都必须有一个合法的名称,可以在初始化时给出,也可以由内核根据“bus name + device ID”的方式创造。

type,struct device_type结构是新版本内核新引入的一个结构,它和struct device关系,非常类似stuct kobj_type和struct kobject之间的关系,后续会再详细说明。

bus,该device属于哪个总线(后续会详细描述)。

driver,该device对应的device driver。

platform_data,一个指针,用于保存具体的平台相关的数据。具体的driver模块,可以将一些私有的数据,暂存在这里,需要使用的时候,再拿出来,因此设备模型并不关心该指针得实际含义。

power、pm_domain,电源管理相关的逻辑,后续会由电源管理专题讲解。

pins,"PINCTRL”功能,暂不描述。

numa_node,"NUMA”功能,暂不描述。

dma_mask~archdata,DMA相关的功能,暂不描述。

devt,dev_t是一个32位的整数,它由两个部分(Major和Minor)组成,在需要以设备节点的形式(字符设备和块设备)向用户空间提供接口的设备中,当作设备号使用。在这里,该变量主要用于在sys文件系统中,为每个具有设备号的device,创建/sys/dev/* 下的对应目录,如下:

1|root@android:/storage/sdcard0 #ls /sys/dev/char/1\:                                                                     
1:1/  1:11/ 1:13/ 1:14/ 1:2/  1:3/  1:5/  1:7/  1:8/  1:9/ 
1|root@android:/storage/sdcard0 #ls /sys/dev/char/1:1                                                                    
1:1/  1:11/ 1:13/ 1:14/
1|root@android:/storage/sdcard0 # ls /sys/dev/char/1\:1 
/sys/dev/char/1:1  

class,该设备属于哪个class。

groups,该设备的默认attribute集合。将会在设备注册时自动在sysfs中创建对应的文件。

 

  • struct device_driver
 1: /* include/linux/device.h, line 213 */
 2: struct device_driver {  
 3:     const char *name;  
 4:     struct bus_type     *bus;
 5:  
 6:     struct module       *owner;
 7:     const char *mod_name; /* used for built-in modules */
 8:  
 9:     bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
 10:  
 11:    const struct of_device_id   *of_match_table;
 12:    const struct acpi_device_id *acpi_match_table;
 13:  
 14:    int (*probe) (struct device *dev);
 15:    int (*remove) (struct device *dev);
 16:    void (*shutdown) (struct device *dev);
 17:    int (*suspend) (struct device *dev, pm_message_t state);
 18:    int (*resume) (struct device *dev);
 19:    const struct attribute_group **groups;
 20:  
 21:    const struct dev_pm_ops *pm;
 22:  
 23:    struct driver_private *p;
 24: };

device_driver就简单多了(在早期的内核版本中driver的数据结构为"struct driver”,不知道从哪个版本开始,就改成device_driver了):

name,该driver的名称。和device结构一样,该名称非常重要,后面会再详细说明。

bus,该driver所驱动设备的总线设备。为什么driver需要记录总线设备的指针呢?因为内核要保证在driver运行前,设备所依赖的总线能够正确初始化。

owner、mod_name,內核module相关的变量,暂不描述。

suppress_bind_attrs,是不在sysfs中启用bind和unbind attribute,如下:root@android:/storage/sdcard0 # ls /sys/bus/platform/drivers/switch-gpio/                                                  
bind   uevent unbind
在kernel中,bind/unbind是从用户空间手动的为driver绑定/解绑定指定的设备的机制。这种机制是在bus.c中完成的,后面会详细解释。

probe、remove,这两个接口函数用于实现driver逻辑的开始和结束。Driver是一段软件code,因此会有开始和结束两个代码逻辑,就像PC程序,会有一个main函数,main函数的开始就是开始,return的地方就是结束。而内核driver却有其特殊性:在设备模型的结构下,只有driver和device同时存在时,才需要开始执行driver的代码逻辑。这也是probe和remove两个接口名称的由来:检测到了设备和移除了设备(就是为热拔插起的!)。

shutdown、suspend、resume、pm,电源管理相关的内容,会在电源管理专题中详细说明。

groups,和struct device结构中的同名变量类似,driver也可以定义一些默认attribute,这样在将driver注册到内核中时,内核设备模型部分的代码(driver/base/driver.c)会自动将这些attribute添加到sysfs中。

p,driver core的私有数据指针,其它模块不能访问。

3. 设备模型框架下驱动开发的基本步骤

在设备模型框架下,设备驱动的开发是一件很简单的事情,主要包括2个步骤:

步骤1:分配一个struct device类型的变量,填充必要的信息后,把它注册到内核中。

步骤2:分配一个struct device_driver类型的变量,填充必要的信息后,把它注册到内核中。

这两步完成后,内核会在合适的时机(后面会讲),调用struct device_driver变量中的probe、remove、suspend、resume等回调函数,从而触发或者终结设备驱动的执行。而所有的驱动程序逻辑,都会由这些回调函数实现,此时,驱动开发者眼中便不再有“设备模型”,转而只关心驱动本身的实现。

以上两个步骤的补充说明:

1. 一般情况下,Linux驱动开发很少直接使用device和device_driver,因为内核在它们之上又封装了一层,如soc device、platform device等等,而这些层次提供的接口更为简单、易用(也正是因为这个原因,本文并不会过多涉及device、device_driver等模块的实现细节)。

2. 内核提供很多struct device结构的操作接口(具体可以参考include/linux/device.h和drivers/base/core.c的代码),主要包括初始化(device_initialize)、注册到内核(device_register)、分配存储空间+初始化+注册到内核(device_create)等等,可以根据需要使用。

3. device和device_driver必须具备相同的名称,内核才能完成匹配操作,进而调用device_driver中的相应接口。这里的同名,作用范围是同一个bus下的所有device和device_driver

4. device和device_driver必须挂载在一个bus之下,该bus可以是实际存在的,也可以是虚拟的。

5. driver开发者可以在struct device变量中,保存描述设备特征的信息,如寻址空间、依赖的GPIOs等,因为device指针会在执行probe等接口时传入,这时driver就可以根据这些信息,执行相应的逻辑操作了。

4. 设备驱动probe的时机

所谓的"probe”,是指在Linux内核中,如果存在相同名称的device和device_driver(注:还存在其它方式,我们先不关注了),内核就会执行device_driver中的probe回调函数,而该函数就是所有driver的入口,可以执行诸如硬件设备初始化、字符设备注册、设备文件操作ops注册等动作("remove”是它的反操作,发生在device或者device_driver任何一方从内核注销时,其原理类似,就不再单独说明了)。

设备驱动prove的时机有如下几种(分为自动触发和手动触发):

  • 将struct device类型的变量注册到内核中时自动触发(device_register,device_add,device_create_vargs,device_create)
  • 将struct device_driver类型的变量注册到内核中时自动触发(driver_register)
  • 手动查找同一bus下的所有device_driver,如果有和指定device同名的driver,执行probe操作(device_attach)
  • 手动查找同一bus下的所有device,如果有和指定driver同名的device,执行probe操作(driver_attach)
  • 自行调用driver的probe接口,并在该接口中将该driver绑定到某个device结构中----即设置dev->driver(device_bind_driver)

注2:probe动作实际是由bus模块(会在下一篇文章讲解)实现的,这不难理解:device和device_driver都是挂载在bus这根线上,因此只有bus最清楚应该为哪些device、哪些driver配对。

注3:每个bus都有一个drivers_autoprobe变量,用于控制是否在device或者driver注册时,自动probe。该变量默认为1(即自动probe),bus模块将它开放到sysfs中了,因而可在用户空间修改,进而控制probe行为。

5. 其它杂项

5.1 device_attribute和driver_attribute

在"Linux设备模型(4)_sysfs”中,我们有讲到,大多数时候,attribute文件的读写数据流为:vfs---->sysfs---->kobject---->attibute---->kobj_type---->sysfs_ops---->xxx_attribute,其中kobj_type、sysfs_ops和xxx_attribute都是由包含kobject的上层数据结构实现。

Linux内核中关于该内容的例证到处都是,device也不无例外的提供了这种例子,如下:

 1: /* driver/base/core.c, line 118 */
 2: static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr,
 3: char *buf) 
 4: {   
 5:     struct device_attribute *dev_attr = to_dev_attr(attr);
 6:     struct device *dev = kobj_to_dev(kobj);
 7:     ssize_t ret = -EIO;
 8: 
 9:     if (dev_attr->show)
 10:        ret = dev_attr->show(dev, dev_attr, buf);
 11:        if (ret >= (ssize_t)PAGE_SIZE) {
 12:            print_symbol("dev_attr_show: %s returned bad count\n",
 13:                        (unsigned long)dev_attr->show);
 14:    }
 15:    return ret;
 16: }
 17:  
 18: static ssize_t dev_attr_store(struct kobject *kobj, struct attribute *attr,
 19: const char *buf, size_t count)
 20: {
 21:    struct device_attribute *dev_attr = to_dev_attr(attr);
 22:    struct device *dev = kobj_to_dev(kobj);
 23:    ssize_t ret = -EIO;
 24: 
 25:    if (dev_attr->store)
 26:        ret = dev_attr->store(dev, dev_attr, buf, count);
 27:    return ret;
 28: }
 29:  
 30: static const struct sysfs_ops dev_sysfs_ops = {
 31:    .show   = dev_attr_show,
 32:    .store  = dev_attr_store,
 33: };
 34:  
 35: /* driver/base/core.c, line 243 */
 36: static struct kobj_type device_ktype = {
 37:    .release    = device_release,
 38:    .sysfs_ops  = &dev_sysfs_ops,
 39:    .namespace = device_namespace,
 40: };
 41:  
 42: /* include/linux/device.h, line 478 */
 43: /* interface for exporting device attributes */
 44: struct device_attribute {
 45:    struct attribute    attr;
 46:    ssize_t (*show)(struct device *dev, struct device_attribute *attr,
 47:                    char *buf);
 48:    ssize_t (*store)(struct device *dev, struct device_attribute *attr,
 49:                    const char *buf, size_t count);
 50: };

 

至于driver的attribute,则要简单的多,其数据流为:vfs---->sysfs---->kobject---->attribute---->driver_attribute,如下:

 1: /* include/linux/device.h, line 247 */
 2: /* sysfs interface for exporting driver attributes */
 3:  
 4: struct driver_attribute {
 5:     struct attribute attr;
 6:     ssize_t (*show)(struct device_driver *driver, char *buf);
 7:     ssize_t (*store)(struct device_driver *driver, const char *buf,
 8:                     size_t count);
 9: };
 10:  
 11: #define DRIVER_ATTR(_name, _mode, _show, _store)    \
 12: struct driver_attribute driver_attr_##_name =       \
 13:    __ATTR(_name, _mode, _show, _store)

5.2 device_type

device_type是内嵌在struct device结构中的一个数据结构,用于指明设备的类型,并提供一些额外的辅助功能。它的的形式如下:

 1: /* include/linux/device.h, line 467 */
 2: struct device_type {
 3:     const char *name;
 4:     const struct attribute_group **groups;
 5:     int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
 6:     char *(*devnode)(struct device *dev, umode_t *mode,
 7:                     kuid_t *uid, kgid_t *gid);
 8:     void (*release)(struct device *dev);
 9:  
 10:    const struct dev_pm_ops *pm;
 11: };

device_type的功能包括:

  • name表示该类型的名称,当该类型的设备添加到内核时,内核会发出"DEVTYPE=‘name’”类型的uevent,告知用户空间某个类型的设备available了
  • groups,该类型设备的公共attribute集合。设备注册时,会同时注册这些attribute。这就是面向对象中“继承”的概念
  • uevent,同理,所有相同类型的设备,会有一些共有的uevent需要发送,由该接口实现
  • devnode,devtmpfs有关的内容,暂不说明
  • release,如果device结构没有提供release接口,就要查询它所属的type是否提供。用于释放device变量所占的空间

5.3 root device

在sysfs中有这样一个目录:/sys/devices,系统中所有的设备,都归集在该目录下。有些设备,是通过device_register注册到Kernel并体现在/sys/devices/xxx/下。但有时候我们仅仅需要在/sys/devices/下注册一个目录,该目录不代表任何的实体设备,这时可以使用下面的接口:

 1: /* include/linux/device.h, line 859 */
 2: /*
 3:  * Root device objects for grouping under /sys/devices
 4:  */
 5: extern struct device *__root_device_register(const char *name,
 6: struct module *owner);
 7:  
 8: /*
 9:  * This is a macro to avoid include problems with THIS_MODULE,
 10:  * just as per what is done for device_schedule_callback() above.
 11:  */
 12: #define root_device_register(name) \
 13: __root_device_register(name, THIS_MODULE)
 14:  
 15: extern void root_device_unregister(struct device *root);

该接口会调用device_register函数,向内核中注册一个设备,但是(你也想到了),没必要注册与之对应的driver(顺便提一下,内核中有很多不需要driver的设备,这是之一)。

 

原创文章,转发请注明出处。蜗窝科技,www.wowotech.net

标签: Linux 内核 设备模型 Device driver

评论:

freeman
2016-08-25 14:23
"想知道的越多,得到的反而越少",说得没错,慢慢来比较快!赞! 评论真的很精彩
wowo
2016-08-26 09:54
@freeman:多谢~~
Robin--
2016-07-02 23:20
@wowo:请教,关于attribute的读写流程中“kobject---->attibute”,这里的attibute是对应哪里?对照《Linux设备模型(4)_sysfs》中,从sysfs_open_file()函数中获取object后,直接就到Kset了,获取 sysfs_ops后,在sysfs_read/write_file()时就到xxx_attibute了,您写这个流程怎么理解?
wowo
2016-07-03 09:48
@Robin--:建议看这个文章,http://www.wowotech.net/linux_kenrel/dm_sysfs.html,里面已经写了。
liuchang5772
2016-05-05 21:17
今天又想到个问题:用device_create()函数创建设备的时候会在/dev目录下生成设备文件,但是并没有指定是块设备还是字符设备,那内核是怎么知道需要创建哪种类型的设备呢?
liuchang5772
2016-05-05 21:56
@liuchang5772:哦 应该是和 device结构体下的class中的dev_kobj有关,默认是字符设备。
我再看看代码。
linuxer
2016-05-06 14:52
@liuchang5772:/dev目录下的设备文件是systemd-udev这个用户空间进程根据内核来的uevent消息而创建的,systemd-udev本身可以通过这些uevent消息来判断一个设备是字符设备还是块设备。一个简单的例子就是:block device传递的uevent消息中会有"SYBSTSTEM=block"
liuchang5772
2016-04-26 10:13
@蜗蜗  我是linux初学者,看你你的bus、device、device_driver 章节,能看明白。下面是典型的一个字符驱动程序,大部分初学者应该都接触过这个程序,现在比较困惑的就是统一设备模型和下面的字符驱动模型是怎么关联起来的?文章中也没有看到填充file_operation结构体的操作。能不能在这个基础上修改一下,加入bus、device、device_driver,用统一设备模型来描述这个驱动程序?

字符驱动链接:
http://www.oschina.net/code/snippet_262623_9616
unique
2016-04-26 10:35
@liuchang5772:这位兄弟的要求有点太那个了,你可以先自己试着写一下,遇到问题请博主帮忙可以,总不能让博主直接帮你写吧?
liuchang5772
2016-04-26 10:59
@unique:@unique  字符驱动已经写过了,但是不知道怎么加到模型里面。原来的方法是通过cdev_add()函数可以将file_operation 结构体注册到系统中,然后可以执行open 、read 等操作,换成统一模型之后没有看到有相关操作,所以想让博主指点一下。
wowo
2016-04-26 16:25
@liuchang5772:这里有我之前写过的一个关于device和device_driver的demo,你可以参考一下(需要完善一下Makefile才能编译):
https://github.com/wowotech/sparrow
liuchang5772
2016-04-27 14:24
@wowo:感谢博主指点,这样就全都能串起来了。将原来驱动中的初始化部分写到probe,去初始化部分写到remove,这是/dev系统和/sys系统关联的关键的部分。
wowo
2016-04-28 08:54
@liuchang5772:不客气:-)
Musiclover
2016-03-27 10:44
看完device 和 device_driver 之后有个疑问,既然sysfs可以将device的属性导出到用户空间,那是不是说一些简单的设备可以直接使用sysfs_ops操作而不用专门写device_driver了呢?device_driver的本质是不是也是实现设备的读写和向用户空间提供接口呢?(问题比较幼稚,见笑)
wowo
2016-03-27 11:37
@Musiclover:Hi Musiclover,这是个非常好的问题,一点都不幼稚,说明你已经有自己的思考了,恭喜恭喜。你说得对,如果某个设备只是提供一些简单的功能,没有device driver也是可以的。不过这样单独存在的device在kernel中应该不对(一时半会我找不到例子)。不过,很多device直接使用自己的bus、class等作为driver,和这个思路本质上是一样的。
Musiclover
2016-03-28 06:17
@wowo:多谢wowo的鼓励和帮助,我会继续努力。
LornYin
2017-08-01 09:50
@wowo:“如果某个设备只是提供一些简单的功能,没有device driver也是可以的。不过这样单独存在的device在kernel中应该不对(一时半会我找不到例子)”  前几天看到单总线的ds18b20,感觉就是wowo说的这种情况,没有走封装好的设备模型,在他的注册里只有个sysfs_create_bin_file(&sl->dev.kobj,&(w1_f29_sysfs_bin_files[i]));   (w1_ds2408.c)
buls1542
2016-03-20 08:45
刚学习驱动没几天,正看字符设备驱动。里面有个CDEV结构体请问窝窝这个和DEVICE与device_driver有什么联系吗?因为我在尝试写sull驱动的时候都没有发现上面两个结构体的定义,现在感觉比较模糊
郭健
2016-03-20 23:56
@buls1542:没有什么关系,一个是用于VFS层的抽象,和用户空间接口相关,一个是设备驱动模型的抽象。
zoon
2015-10-20 18:41
对我很有帮助,不过我没找到vfs到driver_attribute的过程(show, store)
如果有建立数据结构,分析下调用过程就更好了
wowo
2015-10-21 09:05
@zoon:您可以参考下面puppypyb同学的回复,这篇文章只是简单的总结一下,具体的流程大家可以照着代码自己理解,因为只有自己理解过之后,才会设备模型有比较深刻的认识。
tom
2015-10-16 11:24
这篇文章根本就是翻译了一下源码的注释,根本没有走心,例如数据结构的建立,调用流程的分析,都没有
linuxer
2015-10-16 12:07
@tom:device和driver是linux设备模型的核心,是一个高度抽象的模块,我们对它的理解也是在慢慢加深的,有时间会出Version II的,^_^

多谢您的建议和批评,我们会努力的。
puppypyb
2014-12-03 16:47
大多数时候,attribute文件的读写数据流为:vfs---->sysfs---->kobject---->attibute---->kobj_type---->sysfs_ops---->xxx_attribute,其中kobj_type、sysfs_ops和xxx_attribute都是由包含kobject的上层数据结构实现。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
vfs---->sysfs---->kobject---->attibute---->kobj_type---->sysfs_ops---->xxx_attribute,看了代码,wowo你描述的这个流程没有问题,但是有个前提:必须要先用sysfs_create_group来为kobj建立attribute_group关联。因为真正最后调用的是该kobj对应的attribute_group中的show和store。
这部分在设备模型4最后一段好像有写,但描述得比较隐晦。
wowo
2014-12-03 21:58
@puppypyb:抱歉,今天比较忙,没来得及回复。设备模型4中,我确实没有关注attribute group的,我再去看一下代码确认一下。
另外,你也可以把你的理解贴在这里和大家分享一下。
wowo
2014-12-04 22:53
@puppypyb:sysfs中,attribute group是另一个概念。正常情况下,使用sysfs_create_file已经够了。如果还需要把这些attribute文件组织起来,放到一个目录中,那么group是其中的一个方法。因此您这里所说的“先用sysfs_create_group来为kobj建立attribute_group关联”,不是必须的。
另外我在“Linux设备模型(4)_sysfs”有对您的这个评论专门解释,希望继续交流。
puppypyb
2014-12-08 16:01
@wowo:让你见笑了,sysfs_create_file我都没太注意。
  我是在看sysfs那一章节的同时产生的疑问,具体是fill_read_buffer函数中的count = ops->show(kobj, attr_sd->s_attr.attr, buffer->page)这一句。这里ops->show就是ktype中sysfs_ops的show,跟进去以后发觉ktype的show和store其实没做具体的事情。kernel中所有的ktype的show及store无一例外的都是类似下面这样:
static ssize_t xxx_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
    struct xxx_attribute *yyy;
    ...

    yyy = container_of(attr, struct xxx_attribute, attr);
    if (yyy->show)
    ret = yyy->show(kobj, buf);
    
    ...
}
基本上就是一个思想:通过container_of获取attr的上层struct,然后再调用上层的show方法,而attr的上层struct又总是象个样子:
struct xxx_attribute {
    struct  attribute  attr;
    ssize_t (*show)(...);
    ssize_t (*store)(...);
};
例如 bus_attribute, device_attribute, kobj_attribute等等。

所以sysfs的read/write真正关联的是kobj_attribute、device_attribute这些。 关键的操作是在sysfs_add_file_mode()函数中sd->s_attr.attr = (void *)attr这一条语句(这里有些猜测的成分,因为只看到了s_attr.attr部分,wowo能帮助确认下?),sysfs_create_file以及sysfs_create_group都会调用到sysfs_add_file_mode来建立关联。
但是我看到sysfs_create_file中对ktype的default_attrs进行判断,若为NULL则不会建立这种关联。

ps:kernel中有一个例子 kobject-example.c,里面很典型的使用了sysfs_create_group这种方式。
wowo
2014-12-09 22:03
@puppypyb:是的,你总结的很对。另外:
“所以sysfs的read/write真正关联的是kobj_attribute、device_attribute这些。 关键的操作是在sysfs_add_file_mode()函数中sd->s_attr.attr = (void *)attr这一条语句”
sysfs真正关联的还是attr啊,是struct xxx_attribute中的那个struct attribute变量。除了struct attribute,sysfs应该是六亲不认的。
常山黄豆
2014-10-10 14:08
写得不错,我发现越来越喜欢linux!

发表评论:

Copyright @ 2013-2015 蜗窝科技 All rights reserved. Powered by emlog