Linux设备模型 - linux-4.0-rc1
来源:互联网 发布:sql nvl函数用法 编辑:程序博客网 时间:2024/06/04 00:49
!Warning:请确保能够访问图床Imgur,以正常显示图片
源码基线:linux-4.0-rc1
设备模型概述
设备模型主要完成以下工作:
- 设备分类,以分层的架构对设备进行描述,隐藏设备内部的连接细节,对外清晰地展示可用的设备。
- 创建和管理设备的生命周期。
- 通过sysfs虚拟文件系统,向用户空间提供对设备的读写操作,获取设备的信息、改变设备的运行状态。
设备模型的结构组成:
1.总线。
所有的设备都通过总线相连,包括内部的虚拟总线。在内核中,用struct bus_type
结构体来表示总线。
struct bus_type { const char * name; struct kset drivers; struct kset devices; }
name
是总线的名字。kset drivers
与kset devices
,分别代表了总线的驱动程序及插入总线的所有设备集合。
2.设备。
每个设备实例用struct device结构体来表示。
struct device { struct device *parent; struct kobject kobj; char bus_id[BUS_ID_SIZE]; struct bus_type * bus; struct device_driver *driver; }
- parent指向设备的所属父设备,通常是某种总线。
- kobj表示本设备对象。
- bus_id是总线上标识设备的ID信息,通常由字符串”<域编号>:<总线编号>:<设备编号>:<功能编号>”定义
bus
标识了设备连接在哪个总线上。driver
管理设备的驱动程序。
3.驱动程序。
设备驱动程序设备模型可以跟踪所有注册的设备,驱动程序为设备提供服务,完成设备的工作。设备的驱动程序由结构体struct device_driver定义。
struct device_driver { const char * name; struct bus_type * bus; struct kobject kobj; struct klist klist_devices; }
name
是驱动程序的名字。bus
是指驱动程序所操作的总线类型。kobj
表示驱动程序服务的设备对象。klist_devices
是驱动程序当前能操作的设备链表。
platform虚拟总线、设备、驱动
struct kobject与kset结构体
kobject与kset是组成设备模型的基本结构,为来表示设备模型的设备实例与设备层次关系。这两个结构体作为面向对象概念的基类,嵌套在其他结构体里使用。在sysfs中显示的每一个实例,都对应一个kobject。
/* 设备模型的基本结构。 * 在sysfs中显示的每一个对象,都对应一个kobject,它被用来与内核交互并创建它的可见表述。 * 内核用kobject结构将各个对象连接起来组成一个分层的结构体系 */ struct kobject { const char *name; struct kref kref; /* 对象的引用计数。一个内核对象被创建时,需要知道对象存活 的时间,跟踪生命周期的一个方法是使用引用计数,当内核中没有该对象的引用 时,表示对象的生命周期结束,可以被删除 */ struct list_head entry; /* 连接到kset建立层次结构 */ struct kobject * parent; /* parent保存了分层结构中上一层节点kobject结构 的指针。比如一个kobject结构表示了一个USB设备,它的parent指针可能指向 了表示USB集线器的对象,而USB设备是插在USB集线器上的。parent指针最重 要的用途是在sysfs分层结构中定位对象 */ struct kset * kset; /* 一个kset是嵌入相同类型结构的kobject集合。每个kset 内部,包含了自己的kobject。kset总是在sysfs中出现,一旦设置了kset并 把它添加到系统中,将在sysfs中创建一个目录。kobject不必在sysfs中表示, 但kset中每一个kobject成员都将在sysfs中得到表述 */ struct kobj_type * ktype; /* 属性结构。kset中也有一个ktype,其使用优先于 kobject的此处ktype,因此在典型应用中,kobject中的ktype成员被设置 为NULL */ struct sysfs_dirent * sd; /* 指向sysfs下以kobj.name所命名生成的目录 */ }; struct kset { struct kobj_type *ktype; struct list_head list; spinlock_t list_lock; struct kobject kobj; struct kset_uevent_ops *uevent_ops; };
假设有个总线X-bus,以及3个设备驱动A-driver、B-driver、C-driver,且这3个设备都属于同一类X-bus总线。那么kobject与kset在描述设备模型时,通常这样使用。在X-bus的描述结构体中包含kset结构体作为同一类型设备的集合;在描述设备驱动A、B、C的driver结构体中包含kset指针,指向所属的类型集合。每个driver都有一个kobject来表示驱动实例自身,并把所有集合按照加入的先后顺序通过list链表(kset.list与kobject.entry)连接起来。
注册platform总线
函数入口driver_init()
void __init driver_init(void) { devices_init(); buses_init(); platform_bus_init(); }
devices_init()
devices_init()
创建sysfs模型如下:/sys
├── dev
│ ├── block
│ └── char
└── devices
struct kset *devices_kset; /* /sys/devices/ */static struct kobject *dev_kobj;struct kobject *sysfs_dev_char_kobj;struct kobject *sysfs_dev_block_kobj;int __init devices_init(void){ devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL); dev_kobj = kobject_create_and_add("dev", NULL); sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj); sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);}
kset_create_and_add()
/* 动态创建kset结构体,在sysfs中创建以@name命名的目录,如果kset->kobj.ktype有属性值, 则在目录下创建以属性名命名的文件 */struct kset *kset_create_and_add(const char *name, const struct kset_uevent_ops *uevent_ops, struct kobject *parent_kobj){ struct kset *kset; kset = kset_create(name, uevent_ops, parent_kobj); /* 动态创建kset结构体 */ kset_register(kset); /* 初始化kset,并加入到sysfs */}/* 动态创建kset结构体 */static struct kset *kset_create(const char *name, const struct kset_uevent_ops *uevent_ops, struct kobject *parent_kobj){ struct kset *kset; int retval; kset = kzalloc(sizeof(*kset), GFP_KERNEL); kobject_set_name(&kset->kobj, "%s", name); kset->uevent_ops = uevent_ops; kset->kobj.parent = parent_kobj; kset->kobj.ktype = &kset_ktype; kset->kobj.kset = NULL; return kset;}/* 初始化kset,并加入到sysfs */int kset_register(struct kset * k) { kset_init(k); kobject_add_internal(&k->kobj); kobject_uevent(&k->kobj, KOBJ_ADD); } void kset_init(struct kset * k) { kobject_init_internal(&k->kobj); INIT_LIST_HEAD(&k->list); spin_lock_init(&k->list_lock);} static void kobject_init_internal(struct kobject *kobj){ kref_init(&kobj->kref); /* 初始化kobject的引用计数为1 */ INIT_LIST_HEAD(&kobj->entry); /* 初始化entry链表 */ kobj->state_in_sysfs = 0; kobj->state_add_uevent_sent = 0; kobj->state_remove_uevent_sent = 0; kobj->state_initialized = 1;}/* kobject_add_internal()在sysfs下建立以kobj.name命名的目录 */static int kobject_add_internal(struct kobject *kobj){ struct kobject *parent; parent = kobject_get(kobj->parent); /* join kset if set, use it as parent if we do not already have one */ if (kobj->kset) { if (!parent) parent = kobject_get(&kobj->kset->kobj); kobj_kset_join(kobj); kobj->parent = parent; } create_dir(kobj); kobj->state_in_sysfs = 1;}/* 创建kobject对象的目录及属性文件 */static int create_dir(struct kobject *kobj){ const struct kobj_ns_type_operations *ops; sysfs_create_dir_ns(kobj, kobject_namespace(kobj)); /* 创建目录 */ populate_dir(kobj); /* 创建属性文件 */ sysfs_get(kobj->sd); ops = kobj_child_ns_ops(kobj); if (ops) { sysfs_enable_ns(kobj->sd); }} /* 以kobj.name命名,在sysfs下创建一个目录 */int sysfs_create_dir_ns(struct kobject *kobj, const void *ns){ struct kernfs_node *parent, *kn; /* 指定创建的kobj目录所属的的父目录,默认为sysfs根目录 */ if (kobj->parent) parent = kobj->parent->sd; else parent = sysfs_root_kn; /* 在parent目录下,创建以kobj.name命名的目录 */ kn = kernfs_create_dir_ns(parent, kobject_name(kobj), S_IRWXU | S_IRUGO | S_IXUGO, kobj, ns); kobj->sd = kn;}/* 对象可能是带有属性的,在kobject目录下创建以属性名命名的文件 */static int populate_dir(struct kobject *kobj){ struct kobj_type *t = get_ktype(kobj); struct attribute *attr; int i; if (t && t->default_attrs) { for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) { sysfs_create_file(kobj, attr); } }}
devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL)
创建的模型:
kobject_create_and_add()
/* 动态创建一个kobject对象,并且加入到sysfs */struct kobject *kobject_create_and_add(const char *name, struct kobject *parent){ struct kobject *kobj; kobj = kobject_create(); kobject_add(kobj, parent, "%s", name); return kobj;}struct kobject *kobject_create(void){ struct kobject *kobj; kobj = kzalloc(sizeof(*kobj), GFP_KERNEL); kobject_init(kobj, &dynamic_kobj_ktype); return kobj;}/* 初始化kobject结构体 */void kobject_init(struct kobject *kobj, struct kobj_type *ktype){ kobject_init_internal(kobj); kobj->ktype = ktype;}int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...){ va_list args; va_start(args, fmt); kobject_add_varg(kobj, parent, fmt, args); va_end(args);}static int kobject_add_varg(struct kobject *kobj, struct kobject *parent, const char *fmt, va_list vargs){ kobject_set_name_vargs(kobj, fmt, vargs); kobj->parent = parent; return kobject_add_internal(kobj);}/* 设置@kobj的name */int kobject_set_name_vargs(struct kobject *kobj, const char *fmt, va_list vargs){ char *s; kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs); /* 名字中不能包含有'/'的字符,如果有,则直接截短 */ while ((s = strchr(kobj->name, '/'))) s[0] = '!';}
dev_kobj = kobject_create_and_add("dev", NULL)
创建的模型:
sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj)
与
sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj)
创建的模型:
buses_init()
buses_init()
创建sysfs模型如下:/sys
├── bus
└── devices
└── system
static struct kset *bus_kset;static struct kset *system_kset; /* /sys/devices/system */int __init buses_init(void){ bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL); system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj);}
bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL)
创建的模型:
system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj)
创建的模型:
platform_bus_init()
platform_bus_init()
完成虚拟设备platform,及虚拟总线platform的创建。/sys
└── devices
└── platform
└── uevent /* -rw-r–r– */
/* platform设备 */struct device platform_bus = { .init_name = "platform",};/* platform总线 */struct bus_type platform_bus_type = { .name = "platform", .dev_groups = platform_dev_groups, .match = platform_match, .uevent = platform_uevent, .pm = &platform_dev_pm_ops,};int __init platform_bus_init(void){ early_platform_cleanup(); device_register(&platform_bus); /* 注册到devices目录中 */ bus_register(&platform_bus_type); /* 注册到bus目录中 */ of_platform_register_reconfig_notifier();}
device_register()
/* 注册设备:初始化设备的数据结构,将其加入到数据结构的网络中。 完成设备注册后,可以在/sys/devices目录中看到 */ int device_register(struct device *dev) { device_initialize(dev); /* 初始化dev结构 */ device_add(dev); /* 添加dev至sysfs */ } void device_initialize(struct device *dev) { /* 将设备kobject的kset集合指向devices_kset */ dev->kobj.kset = devices_kset; kobject_init(&dev->kobj, &device_ktype);} #define DEVICE_ATTR_RW(_name) \ struct device_attribute dev_attr_##_name = __ATTR_RW(_name)#define __ATTR_RW(_name) __ATTR(_name, (S_IWUSR | S_IRUGO), \ _name##_show, _name##_store)#define __ATTR(_name, _mode, _show, _store) { \ .attr = {.name = __stringify(_name), \ .mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \ .show = _show, \ .store = _store, \}static DEVICE_ATTR_RW(uevent); /* 定义dev_attr_uevent,其展开宏如下: struct device_attribute dev_attr_uevent { .attr = { .name = "uevent", .mode = (S_IWUSR | S_IRUGO), }, .show = uevent_show, .store = uevent_store, }; */int device_add(struct device *dev) { struct device *parent = NULL; struct kobject *kobj; struct class_interface *class_intf; dev = get_device(dev); /* 将设备的引用计数加1 */ if (!dev->p) { device_private_init(dev); } /* 使用dev.init_name设置dev.kobj.name,并置空dev.init_name */ if (dev->init_name) { dev_set_name(dev, "%s", dev->init_name); dev->init_name = NULL; } /* 如果kobject_add()的第二个参数parent为空,则以dev->kobj.kset.kobj作为父目录 */ kobject_add(&dev->kobj, dev->kobj.parent, NULL); /* dev_attr_uevent由宏DEVICE_ATTR_RW()定义,在dev.kobj目录下创建以 dev_attr_uevent.attr.name命名,权限为dev_attr_uevent.attr.mode的文件 */ device_create_file(dev, &dev_attr_uevent); device_add_class_symlinks(dev); /* 创建类型软链接 */}
device_register()
bus_register(&platform_bus_type)
向系统添加一个新总线。/sys
└── bus
└── platform
├── devices
├── drivers
├── drivers_autoprobe /* -rw-r–r– */
├── drivers_probe /* –w——- */
└── uevent /* –w——- */
```cstruct bus_type { const char * name; /* 总线的文本名称,用于在sysfs文件系统中标识总线 */ /* 试图查找与给定设备匹配的驱动程序 */ int (*match)(struct device * dev, struct device_driver * drv); int (*uevent)(struct device *dev, struct kobj_uevent_env *env); /* 在有必要将驱动程序关联到设备时,会调用probe。该函数检测设备在系统中是否 真正存在 */ int (*probe)(struct device * dev); /* 删除驱动程序和设备之间的关联。例如,在将可热挺拔的设备从系统中移除时,会 调用该函数 */ int (*remove)(struct device * dev); struct subsys_private *p;}; struct bus_type platform_bus_type = { .name = "platform", .dev_groups = platform_dev_groups, .match = platform_match, .uevent = platform_uevent, .pm = &platform_dev_pm_ops,};static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store); /* 定义bus_attr_uevent ,其展开宏如下: struct bus_attribute bus_attr_uevent { .attr = { .name = "uevent", .mode = S_IWUSR, }, .show = NULL, .store = bus_uevent_store, }; *//* 注册新的总线 */ int bus_register(struct bus_type *bus){ int retval; struct subsys_private *priv; struct lock_class_key *key = &bus->lock_key; priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL); priv->bus = bus; bus->p = priv; /* */ kobject_set_name(&priv->subsys.kobj, "%s", bus->name); priv->subsys.kobj.kset = bus_kset; /* 父目录指向bus_kset.kobj */ priv->subsys.kobj.ktype = &bus_ktype; priv->drivers_autoprobe = 1; /* 初始化&priv->subsys,并加入到sysfs */ kset_register(&priv->subsys); /* bus_attr_uevent由宏BUS_ATTR()定义,在priv.subsys.kobj目录下创建以 bus_attr_uevent.attr.name命名,权限为bus_attr_uevent.attr.mode的文件 */ bus_create_file(bus, &bus_attr_uevent); priv->devices_kset = kset_create_and_add("devices", NULL, &priv->subsys.kobj); priv->drivers_kset = kset_create_and_add("drivers", NULL, &priv->subsys.kobj); add_probe_files(bus); /* 创建drivers_probe与drivers_autoprobe文件 */ bus_add_groups(bus, bus->bus_groups);}
最终模型
driver_init()
最终形成的模型,如下所示:/sys
├── bus
│ └── platform
│ ├── devices
│ ├── drivers
│ ├── drivers_autoprobe /* -rw-r–r– */
│ ├── drivers_probe /* –w——- */
│ └── uevent /* –w——- */
├── dev
│ ├── block
│ └── char
└── devices
└── platform
└── uevent /* -rw-r–r– */
- Linux设备模型 - linux-4.0-rc1
- Linux Kernel 4.0 RC1 发布!
- Linux设备模型_platform设备
- linux设备模型详解
- linux设备模型详解
- Linux设备驱动模型
- linux的设备模型
- Linux设备模型
- [转]linux设备模型
- Linux的设备模型
- Linux设备模型
- Linux 设备模型
- Linux设备模型
- linux设备模型分析
- Linux 2.6 设备模型
- Linux设备模型 (1)
- Linux设备模型 (2)
- Linux设备模型 (3)
- 优雅编程之这样使用对象通用方法,你就“正常”了(三十四)
- 极客学院30天免费vip
- go语言特性113
- GT4编程实例
- caffe配置文件 网络lenet-train-test.prototxt注释及说明
- Linux设备模型 - linux-4.0-rc1
- Android简易实战教程--第三十六话《电话录音》
- 你所不知道的 Console
- codeforces round 376 Div2 D~F
- Spring源码阅读-BeanFactory子接口HierarchicalBeanFactory注释翻译
- 第八周项目oj
- 手机客户端弱网络下的断线重连处理
- hibernate或SSH框架怎么在插入数据后拿到自动递增主键的GeneratedKey?
- chorme浏览器完美保存整个网页