| 💡本文基于linux-v4.14.7,文中代码有删减,只保留了核心部分,删除了许多异常的处理
1. 概述
Linux 设备模型(Linux Device Model)是内核中统一描述和管理硬件及其驱动关系的基础框架,核心目标是:抽象硬件、统一驱动模型、支持热插拔、并向用户空间暴露一致的视图(sysfs / uevent)。

2. 数据结构定义

struct bus_type 代表总线
c
struct bus_type {
const char *name; /* 总线的名字 */
struct subsys_private *p; /* 总线私有数据 */
};
struct subsys_private {
struct kset subsys;
struct kset *devices_kset; /* 所有dev都属于这个kset集合 */
struct list_head interfaces;
struct mutex mutex;
struct kset *drivers_kset; /* 所有drv都属于这个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 class *class;
};
struct device 代表设备
c
struct device {
struct device *parent;
struct device_private *p;
struct kobject kobj;
const char *init_name; /* dev名字e */
const struct device_type *type;
struct bus_type *bus; /* 指向所属bus */
struct klist_node knode_class; /* 作为class成员时使用的链表节点 */
struct class *class
};
struct device_private {
struct klist klist_children; /* 链接children devic的链表头节点 */
struct klist_node knode_parent; /* 链接到parent的链接节点 */
struct klist_node knode_driver; /* 链接到绑定驱动的链表节点 */
struct klist_node knode_bus; /* 用于挂到其所属bus的klist_devices链表中 */
struct device *device;
}
struct device_driver 代表驱动
c
struct device_driver {
const char *name;
struct bus_type *bus;
struct module *owner;
enum probe_type probe_type;
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);
struct driver_private *p;
};
struct driver_private {
struct kobject kobj;
struct klist klist_devices; /* 用于链接绑定该驱动的dev节点 */
struct klist_node knode_bus; /* 用于挂到其所属bus的klist_drivers链表中 */
struct module_kobject *mkobj;
struct device_driver *driver;
};
3. 核心操作函数源码分析
当内核启动后,首先会依次调用devices_init 、buses_init、classes_init函数,用于建立设备模型的基础目录结构。
c
/**
* driver_init:
* ├── devices_init
* ├── buses_init
* └── classes_init
*/
int __init devices_init(void)
{
/* /sys/devices */
devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
/* /sys/dev */
dev_kobj = kobject_create_and_add("dev", NULL);
/* /sys/dev/block */
sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
/* /sys/dev/char */
sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
}
int __init buses_init(void)
{
/* /sys/bus */
bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
/* /sys/devices/system */
system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj);
}
int __init classes_init(void)
{
/* /sys/class */
class_kset = kset_create_and_add("class", NULL, NULL);
}
现在目录结构如下

3.1. 注册总线
bus_register 用于注册一个总线,首先将总线的目录注册到/sys/bus/[bus->name],函数内部使用bus_kset和bus_ktype是全局变量,所有总线都指向它。drivers_autoprobe用于控制驱动或者设备加载是是否自动探测。然后创建/sys/bus/[bus->name]文件,/sys/bus/[bus->name]/devices和/sys/bus/[bus->name]/drivers 目录,创建两个链表priv→klist_devices和priv→klist_drivers 用于后续链接驱动和设备,在/sys/bus/[bus->name]/创建drivers_probe、drivers_autoprobe文件和属性组文件。
c
int bus_register(struct bus_type *bus)
{
int retval;
struct subsys_private *priv;
/* 双向关联 */
priv->bus = bus;
bus->p = priv;
/* 设置subsys的属性 */
retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
priv->subsys.kobj.kset = bus_kset;
priv->subsys.kobj.ktype = &bus_ktype;
/* 设置驱动自动探测 */
priv->drivers_autoprobe = 1;
retval = kset_register(&priv->subsys);
/* 创建/sys/bus/[bus->name]/uevent文件 */
retval = bus_create_file(bus, &bus_attr_uevent);
/* 创建/sys/bus/[bus->name]/devices和/sys/bus/[bus->name]/drivers */
priv->devices_kset = kset_create_and_add("devices", NULL,
&priv->subsys.kobj);
priv->drivers_kset = kset_create_and_add("drivers", NULL,
&priv->subsys.kobj);
/* 创建/sys/bus/platform/drivers_probe 和/sys/bus/platform/drivers_probe/drivers_autoprobe */
retval = add_probe_files(bus);
/* 在/sys/bus/[bus->name]/下创建属性组相关文件 */
retval = bus_add_groups(bus, bus->bus_groups);
}
现在/sys/bus/的目录结构如下所示

3.2 device注册
函数bus_register 用于注册一个设备,其完成device结构体的初始化、然后把设备注册进系统。
device_register
├── device_initialize:初始化各种结构体成员
└── device_add:设备上线
device_initialize用于初始化device对象的基础成员,为后续device_add()/device_register()做准备。函数先设置dev的kset指向devices_kset,初始化各种链表和锁,最后将dev的状态设置为没有驱动,此时设备仍然不可见。
c
void device_initialize(struct device *dev)
{
/* 设置kset */
dev->kobj.kset = devices_kset;
kobject_init(&dev->kobj, &device_ktype);
lockdep_set_novalidate_class(&dev->mutex);
/* 状态为无驱动 */
dev->links.status = DL_DEV_NO_DRIVER;
}
device_add这一阶段是"设备正式上线",核心动作包括:设置dev_name,建立parent-child关系,创建 /sys/devices/...目录,挂到bus->p->klist_devices ,触发uevent,尝试和driver进行匹配,从这里开始,设备对内核和用户态都是可见的。
c
int device_add(struct device *dev)
{
struct device *parent;
struct kobject *kobj;
struct class_interface *class_intf;
struct kobject *glue_dir = NULL;
/* 引用计数+1 */
dev = get_device(dev);
/* 初始化私有数据 */
if (!dev->p) {
error = device_private_init(dev);
}
/* 设置name */
if (dev->init_name) {
dev_set_name(dev, "%s", dev->init_name);
dev->init_name = NULL;
}
if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);
if (!dev_name(dev)) {
error = -EINVAL;
goto name_error;
}
parent = get_device(dev->parent);
/* 计算实际的父目录 */
kobj = get_device_parent(dev, parent);
if (kobj)
dev->kobj.parent = kobj;
/* use parent numa_node */
if (parent && (dev_to_node(dev) == NUMA_NO_NODE))
set_dev_node(dev, dev_to_node(parent));
/* 添加到sysfs层次结构中 */
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
/* 创建uevent文件 */
error = device_create_file(dev, &dev_attr_uevent);
/* 建立device和class之间的双向链接 */
error = device_add_class_symlinks(dev);
/* 创建属性文件 */
error = device_add_attrs(dev);
/* 添加dev到bus的链表 */
error = bus_add_device(dev);
/* 电源管理 */
error = dpm_sysfs_add(dev);
device_pm_add(dev);
if (MAJOR(dev->devt)) {
/* 创建文件 */
error = device_create_file(dev, &dev_attr_dev);
error = device_create_sys_dev_entry(dev);
/* 创建字符设备/dev/节点 */
devtmpfs_create_node(dev);
}
/* 内核通知链,ADD事件*/
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_ADD_DEVICE, dev);
/*告知用户空间的ADD事件*/
kobject_uevent(&dev->kobj, KOBJ_ADD);
/* 驱动探测 */
bus_probe_device(dev);
if (parent)
klist_add_tail(&dev->p->knode_parent,
&parent->p->klist_children);
/* class和device绑定 */
if (dev->class) {
mutex_lock(&dev->class->p->mutex);
/* tie the class to the device */
klist_add_tail(&dev->knode_class,
&dev->class->p->klist_devices);
/* notify any interfaces that the device is here */
list_for_each_entry(class_intf,
&dev->class->p->interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
mutex_unlock(&dev->class->p->mutex);
}
}
bus_add_device 建立两个软链接,并将dev加入到bus的链表进行管理。
c
int bus_add_device(struct device *dev)
{
struct bus_type *bus = bus_get(dev->bus);
int error = 0;
if (bus) {
error = device_add_groups(dev, bus->dev_groups);
/* 建立/sys/bus/<bus>/devices/<dev_name> → /sys/devices/.../<dev_name>的软链接 */
error = sysfs_create_link(&bus->p->devices_kset->kobj,
&dev->kobj, dev_name(dev));
/* 建立/sys/devices/.../<dev>/subsystem → /sys/bus/<bus>的软链接 */
error = sysfs_create_link(&dev->kobj,
&dev->bus->p->subsys.kobj, "subsystem");
/* 加入到klist_devices链表 */
klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
}
return 0;
}
3.3. 注册驱动
函数driver_register用于注册一个驱动,首先根据名字在总线里面查找驱动,如果没有同名驱动的话则将驱动注册到总线,创建属性组,生成uevent的ADD事件。
c
/**
* driver_register:
* ├── driver_find:查找同名驱动
* ├── bus_add_driver:
* └── kobject_uevent:生成ADD事件
*/
int driver_register(struct device_driver *drv)
{
int ret;
struct device_driver *other;
/* 查找是否有同名驱动 */
other = driver_find(drv->name, drv->bus);
if (other) {
return -EBUSY;
}
ret = bus_add_driver(drv);
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);
if (ret) {
return ret;
}
kobject_uevent(&drv->p->kobj, KOBJ_ADD);
return ret;
}
上面注册总线时提到一个函数bus_add_driver 是将驱动注册到总线的核心功能实现。
c
int bus_add_driver(struct device_driver *drv)
{
struct bus_type *bus;
struct driver_private *priv;
int error = 0;
/* 总线引用计数+1 */
bus = bus_get(drv->bus);
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
klist_init(&priv->klist_devices, NULL, NULL);
priv->driver = drv;
drv->p = priv;
/* 创建/sys/bus/[bus->name]/drivers/[drv->name]节点 */
priv->kobj.kset = bus->p->drivers_kset;
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
"%s", drv->name);
/* 加入链表管理 */
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
if (drv->bus->p->drivers_autoprobe) {
/* 判断驱动的probe能不能放到异步线程中执行,而不是在同步路径里阻塞内核初始化流程 */
if (driver_allows_async_probing(drv)) {
/* 异步遍历device,调用match函数匹配 */
async_schedule(driver_attach_async, drv);
} else {
/* 同步遍历device,调用match函数匹配 */
error = driver_attach(drv);
if (error)
goto out_unregister;
}
}
module_add_driver(drv->owner, drv);
/* 创建uevent文件 */
error = driver_create_file(drv, &driver_attr_uevent);
/* 创建属性组 */
error = driver_add_groups(drv, bus->drv_groups);
return 0;
}
3.4 驱动和设备的探测
当添加设备时,会通过bus总线去探测驱动,如果设备指定了驱动,那么直接和它绑定;如果设备未指定驱动,那么遍历驱动链表查找,如果找到则调用probe函数执行。

c
static int __device_attach(struct device *dev, bool allow_async)
{
device_lock(dev);
if (dev->driver) { /* 指定了驱动程序 */
/* 设备绑定驱动 */
ret = device_bind_driver(dev);
} else { /* 未指定驱动程序 */
ret = bus_for_each_drv(dev->bus, NULL, &data,
__device_attach_driver);
}
}
设备指定驱动时会调用device_bind_driver 函数,加入链表节点,发送bind事件。
c
static void driver_bound(struct device *dev)
{
/* 将dev加入到klist_devices链表进行管理 */
klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);
/* 发送BIND事件 */
kobject_uevent(&dev->kobj, KOBJ_BIND);
}
设备探测驱动时,如果通过match函数找到匹配的驱动,那么则会调用probe函数
c
static int __device_attach_driver(struct device_driver *drv, void *_data)
{
ret = driver_match_device(drv, dev);
return driver_probe_device(drv, dev);
}
当添加驱动时,driver_attach会去匹配设备,如果匹配成功则调用probe函数

c
int driver_attach(struct device_driver *drv)
{
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
static int __driver_attach(struct device *dev, void *data)
{
struct device_driver *drv = data;
driver_match_device(drv, dev);
driver_probe_device(drv, dev);
return 0;