当前位置:首页>Linux>Linux设备模型

Linux设备模型

  • 2026-10-13 04:20:23
Linux设备模型

1. 概述

Linux 设备模型(Linux Device Model)是内核中统一描述和管理硬件及其驱动关系的基础框架,核心目标是:抽象硬件、统一驱动模型、支持热插拔、并向用户空间暴露一致的视图(sysfs / uevent)。

2. 数据结构定义

struct bus_type 代表总线

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 代表设备

struct device {struct device        *parent;struct device_private    *p;struct kobject kobj;    const char        *init_name; /* dev名字e */    conststruct 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 代表驱动

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函数,用于建立设备模型的基础目录结构。

/** * 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文件和属性组文件。

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的状态设置为没有驱动,此时设备仍然不可见。

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进行匹配,从这里开始,设备对内核和用户态都是可见的。

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的链表进行管理。

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事件。

/** * 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 是将驱动注册到总线的核心功能实现。

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函数执行。

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事件。

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函数

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函数

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;

最新文章

随机文章