title: 设备模型与Kobject tags: [Linux内核, 设备模型, Kobject, sysfs, 嵌入式] created: 2026-09-16
⚠️ 来源说明:本主题不属于《I.MX6U嵌入式Linux驱动开发指南》的讲授范围,内容基于 Linux 内核源码(Linux 4.1.15)整理,为扩展知识。
💡 关联知识: [[03-Linux驱动开发核心/07-platform驱动模型]] | [[03-Linux驱动开发核心/02-设备树语法与实战]] | [[02-嵌入式Linux内核基础/05-内存管理机制]]
Linux内核在发展过程中,设备数量急剧增加,面临以下问题:
| 问题 | 描述 |
|---|---|
| 命名冲突 | 不同总线上的设备可能同名 |
| 设备发现 | 缺乏统一的设备枚举机制 |
| 电源管理 | 无法统一管理设备的休眠/唤醒 |
| 热插拔 | 无法动态响应设备的添加/移除 |
| 拓扑结构 | 无法表达设备间的层级关系 |
设备模型解决了这些问题,提供了:
┌─────────────────────────────────────────────────────────────┐
│ 用户空间 │
├─────────────────────────────────────────────────────────────┤
│ sysfs (/sys) │
├─────────────────────────────────────────────────────────────┤
│ 设备模型核心层 │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ kobject │ │ kset │ │ ktype │ │ 属性 │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
├─────────────────────────────────────────────────────────────┤
│ 总线类型层 │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ pci │ │ usb │ │ platform│ │ i2c │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
├─────────────────────────────────────────────────────────────┤
│ 设备/驱动层 │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ device │ │ driver │ │ class │ │ bus │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
├─────────────────────────────────────────────────────────────┤
│ 硬件层 │
└─────────────────────────────────────────────────────────────┘
Linux设备模型的核心是总线-设备-驱动三元组:
总线 (bus)
├── 设备 (device) ←→ 匹配 (match)
└── 驱动 (driver) ←→ 匹配 (match)
工作流程:
1. 设备注册 → 总线查找匹配的驱动 → 调用驱动的probe()
2. 驱动注册 → 总线查找匹配的设备 → 调用驱动的probe()
| 组件 | 作用 | 关键结构体 |
|---|---|---|
| 总线 | 连接设备和驱动,负责匹配 | struct bus_type |
| 设备 | 描述硬件信息(地址、中断、时钟) | struct device |
| 驱动 | 实现设备操作(打开、读写、关闭) | struct device_driver |
Kobject是Linux设备模型的基础,每个在内核中被管理的对象都包含一个kobject。
/* 内核源码: include/linux/kobject.h */
struct kobject {
const char *name; /* 对象名称 */
struct list_head entry; /* 链入kset的链表节点 */
struct kobject *parent; /* 父kobject */
struct kset *kset; /* 所属的kset */
struct kobj_type *ktype; /* 类型操作函数 */
struct kernfs_node *sd; /* sysfs节点 */
struct kref kref; /* 引用计数 */
/* 初始化标志 */
unsigned int state_initialized:1; /* 是否已初始化 */
unsigned int state_in_sysfs:1; /* 是否已添加到sysfs */
unsigned int state_add_uevent_sent:1; /* 是否发送过ADD事件 */
unsigned int state_remove_uevent_sent:1; /* 是否发送过REMOVE事件 */
unsigned int uevent_suppress:1; /* 是否抑制uevent */
};
/* 命名示例 */
kobject name → /sys/xxx/
├── parent_kobj → /sys/parent/xxx/
├── kset → /sys/kset_name/xxx/
└── ktype → 决定属性文件
命名冲突解决:通过总线名+设备名区分
/sys/bus/platform/devices/2000000.aips-bus/20a8000.spi
/sys/bus/spi/devices/spi0.0
kobject生命周期状态机:
[未初始化] ──kobject_init()──→ [已初始化] ──kobject_add()──→ [已添加]
│ │ │
│ │ │
↓ ↓ ↓
[销毁] ←──kobject_put()─── [释放] ←──kobject_del()─── [已删除]
/* 初始化kobject(不注册到sysfs) */
void kobject_init(struct kobject *kobj, struct kobj_type *ktype);
/* 将kobject添加到sysfs */
int kobject_add(struct kobject *kobj, struct kobject *parent,
const char *fmt, ...);
/* 原子操作:初始化+添加 */
int kobject_init_and_add(struct kobject *kobj,
struct kobj_type *ktype,
struct kobject *parent,
const char *fmt, ...);
/* 从sysfs移除kobject */
void kobject_del(struct kobject *kobj);
/* 增加引用计数 */
struct kobject *kobject_get(struct kobject *kobj);
/* 减少引用计数,为0时释放 */
void kobject_put(struct kobject *kobj);
⚠️ 来源说明:kobject 的生命周期与引用计数规则为内核源码级细节,不属于教材讲授范围,基于 Linux 4.1.15 源码(
include/linux/kobject.h、include/linux/kref.h)整理。
Kobject使用kref进行引用计数管理:
/* 内核源码: include/linux/kref.h */
struct kref {
atomic_t refcount; /* 原子引用计数 */
};
/* 初始化引用计数为1 */
void kref_init(struct kref *kref);
/* 增加引用计数 */
void kref_get(struct kref *kref);
/* 减少引用计数,返回是否为0 */
int kref_put(struct kref *kref, void (*release)(struct kref *kref));
引用计数规则:
kobject_init()后引用计数为1kobject_add()不增加引用计数kobject_get()增加引用计数kobject_put()减少引用计数引用计数为0时调用release()释放对象
/* 正确使用示例 */
static void my_release(struct kobject *kobj)
{
struct my_device *dev = container_of(kobj, struct my_device, kobj);
kfree(dev);
}
static int __init my_init(void)
{
struct my_device *dev;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
/* 初始化kobject,引用计数=1 */
kobject_init_and_add(&dev->kobj, &my_ktype, NULL, "my_device");
/* 使用完毕后减少引用计数 */
kobject_put(&dev->kobj); /* 引用计数=0,调用my_release */
return 0;
}
/* 完整的kobject使用示例 */
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
struct my_obj {
struct kobject kobj;
int value;
};
/* 属性show函数 */
static ssize_t value_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
struct my_obj *obj = container_of(kobj, struct my_obj, kobj);
return sysfs_emit(buf, "%d\n", obj->value);
}
/* 属性store函数 */
static ssize_t value_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
struct my_obj *obj = container_of(kobj, struct my_obj, kobj);
int ret;
ret = kstrtoint(buf, 10, &obj->value);
if (ret)
return ret;
return count;
}
/* 定义属性 */
static struct kobj_attribute value_attr =
__ATTR(value, 0644, value_show, value_store);
/* 属性数组 */
static struct attribute *my_attrs[] = {
&value_attr.attr,
NULL,
};
/* 属性组 */
static struct attribute_group my_attr_group = {
.attrs = my_attrs,
};
/* ktype定义 */
static struct kobj_type my_ktype = {
.release = my_release,
.sysfs_ops = &kobj_sysfs_ops,
.default_attrs = my_attrs,
};
/* release函数 */
static void my_release(struct kobject *kobj)
{
struct my_obj *obj = container_of(kobj, struct my_obj, kobj);
printk("my_obj released\n");
kfree(obj);
}
/* 模块初始化 */
static struct my_obj *my_obj;
static int __init my_init(void)
{
int ret;
my_obj = kzalloc(sizeof(*my_obj), GFP_KERNEL);
if (!my_obj)
return -ENOMEM;
/* 初始化kobject */
ret = kobject_init_and_add(&my_obj->kobj, &my_ktype, NULL, "my_obj");
if (ret) {
kfree(my_obj);
return ret;
}
printk("my_obj created\n");
return 0;
}
static void __exit my_exit(void)
{
/* 释放kobject */
kobject_put(&my_obj->kobj);
printk("my_obj module removed\n");
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("嵌入式Linux");
Kset是同类型kobject的集合,用于组织和管理相关的kobject。
/* 内核源码: include/linux/kobject.h */
struct kset {
struct list_head list; /* kset中所有kobject的链表 */
spinlock_t list_lock; /* 链表锁 */
struct kobject kobj; /* 内嵌的kobject(用于sysfs) */
const struct kset_uevent_ops *uevent_ops; /* uevent操作函数 */
};
/* uevent操作函数 */
struct kset_uevent_ops {
int (*filter)(struct kset *kset, struct kobject *kobj);
const char *(*name)(struct kset *kset, struct kobject *kobj);
int (*uevent)(struct kset *kset, struct kobject *kobj,
struct kobj_uevent_env *env);
};
/* 创建并初始化kset */
struct kset *kset_create_and_add(const char *name,
const struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj);
/* 初始化kset */
void kset_init(struct kset *k);
/* 添加kset到sysfs */
int kset_register(struct kset *k);
/* 从sysfs移除kset */
void kset_unregister(struct kset *k);
/* 查找kset中的kobject */
struct kobject *kset_find_obj(struct kset *k, const char *name);
Ktype定义了kobject的属性和操作函数。
/* 内核源码: include/linux/kobject.h(Linux 4.1.15) */
struct kobj_type {
void (*release)(struct kobject *kobj); /* 释放函数 */
const struct sysfs_ops *sysfs_ops; /* sysfs操作函数 */
struct attribute **default_attrs; /* 默认属性 */
const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
const void *(*namespace)(struct kobject *kobj);
};
/* sysfs操作函数 */
struct sysfs_ops {
ssize_t (*show)(struct kobject *, struct attribute *, char *);
ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t);
};
⚠️ 版本差异:
default_groups字段是 4.9+ 才加入kobj_type的;Linux 4.1.15 只能用default_attrs指定默认属性(属性组需在 kobject 添加后另行sysfs_create_groups())。下文示例已按 4.1.15 调整为default_attrs。
┌─────────────────────────────────────────────────────────────┐
│ kset │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ kobject (kset自身) │ │
│ │ name: "my_kset" │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ list: ──→ [kobject1] ──→ [kobject2] ──→ [kobject3] ──→ │
│ │
│ uevent_ops: 过滤、命名、事件处理 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ kobject (子对象) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ name: "my_device0" │ │
│ │ parent: → 父kobject │ │
│ │ kset: → 所属kset │ │
│ │ ktype: → 类型操作 │ │
│ │ kref: 引用计数 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ sysfs节点: /sys/kset_name/my_device0/ │
│ 属性文件: value, status, ... (由ktype的default_attrs决定) │
└─────────────────────────────────────────────────────────────┘
#include <linux/module.h>
#include <linux/kobject.h>
static struct kset *my_kset;
struct my_device {
struct kobject kobj;
int id;
char name[32];
};
static ssize_t name_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
struct my_device *dev = container_of(kobj, struct my_device, kobj);
return sysfs_emit(buf, "%s\n", dev->name);
}
static ssize_t id_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
struct my_device *dev = container_of(kobj, struct my_device, kobj);
return sysfs_emit(buf, "%d\n", dev->id);
}
static struct kobj_attribute name_attr = __ATTR_RO(name);
static struct kobj_attribute id_attr = __ATTR_RO(id);
static struct attribute *my_attrs[] = {
&name_attr.attr,
&id_attr.attr,
NULL,
};
static struct attribute_group my_attr_group = {
.attrs = my_attrs,
};
static struct kobj_type my_ktype = {
.release = my_release,
.sysfs_ops = &kobj_sysfs_ops,
.default_attrs = my_attrs, /* 4.1.15 用 default_attrs;default_groups 为 4.9+ */
};
static int my_uevent_filter(struct kset *kset, struct kobject *kobj)
{
return 1;
}
static const char *my_uevent_name(struct kset *kset, struct kobject *kobj)
{
return "MY_DEVICE";
}
static struct kset_uevent_ops my_uevent_ops = {
.filter = my_uevent_filter,
.name = my_uevent_name,
};
static int __init my_init(void)
{
struct my_device *dev;
my_kset = kset_create_and_add("my_devices", &my_uevent_ops, NULL);
if (!my_kset)
return -ENOMEM;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
kset_unregister(my_kset);
return -ENOMEM;
}
dev->id = 1;
strcpy(dev->name, "sensor0");
kobject_init_and_add(&dev->kobj, &my_ktype, &my_kset->kobj, "sensor0");
printk("kset created, device added\n");
return 0;
}
static void __exit my_exit(void)
{
kset_unregister(my_kset);
printk("kset removed\n");
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
sysfs是内核向用户空间导出设备信息的虚拟文件系统。
# 挂载sysfs
mount -t sysfs sysfs /sys
# 查看sysfs结构
ls -la /sys/
# bus/ class/ devices/ firmware/ kernel/ module/ power/
sysfs目录结构:
/sys
├── block/ # 块设备
├── bus/ # 总线类型
│ ├── i2c/
│ ├── platform/
│ ├── spi/
│ └── usb/
├── class/ # 设备类
│ ├── input/
│ ├── net/
│ └── tty/
├── devices/ # 设备层次结构
│ └── system/
│ ├── cpu/
│ └── memory/
├── firmware/ # 固件接口
├── kernel/ # 内核参数
├── module/ # 模块信息
└── power/ # 电源管理
设备属性通过sysfs文件系统暴露给用户空间。
/* 属性类型 */
struct attribute {
const char *name; /* 属性名 */
umode_t mode; /* 权限 */
};
/* kobj属性 */
struct kobj_attribute {
struct attribute attr;
ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count);
};
/* 设备属性 */
struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
};
/* 宏定义(Linux 4.1.15,位于 include/linux/sysfs.h,均为聚合初始化器) */
#define __ATTR(_name, _mode, _show, _store) { \
.attr = { .name = __stringify(_name), \
.mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \
.show = _show, \
.store = _store, \
}
#define __ATTR_RO(_name) { \
.attr = { .name = __stringify(_name), .mode = S_IRUGO }, \
.show = _name##_show, \
}
#define __ATTR_RW(_name) __ATTR(_name, (S_IWUSR | S_IRUGO), \
_name##_show, _name##_store)
⚠️ 易错点:
__ATTR_INITIALIZER/__ATTR_RO_INITIALIZER这类宏在内核中并不存在(属编造);__ATTR*直接展开为结构体聚合初始化,见 4.1.15 的include/linux/sysfs.h。
/* 用户空间读取文件 → 内核调用show() */
ssize_t show_func(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
/* buf是内核分配的缓冲区,最多PAGE_SIZE-1字节 */
return sysfs_emit(buf, "value: %d\n", some_value);
}
/* 用户空间写入文件 → 内核调用store() */
ssize_t store_func(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
int val;
int ret;
/* 使用安全的转换函数 */
ret = kstrtoint(buf, 10, &val);
if (ret)
return ret;
some_value = val;
return count;
}
/* 创建属性文件 */
static struct kobj_attribute my_attr = __ATTR_RW(my);
/* 批量创建 */
static struct attribute *my_attrs[] = {
&my_attr.attr,
NULL,
};
static struct attribute_group my_attr_group = {
.attrs = my_attrs,
};
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
static struct kobject *my_kobj;
static int led_state = 0;
static char led_color[32] = "red";
static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
return sysfs_emit(buf, "%d\n", led_state);
}
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
int ret;
ret = kstrtoint(buf, 10, &led_state);
if (ret)
return ret;
printk("LED state set to %d\n", led_state);
return count;
}
static ssize_t color_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
return sysfs_emit(buf, "%s\n", led_color);
}
static ssize_t color_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
size_t len = min(count, sizeof(led_color) - 1);
memcpy(led_color, buf, len);
led_color[len] = '\0';
printk("LED color set to %s\n", led_color);
return count;
}
static struct kobj_attribute state_attr = __ATTR(state, 0644, state_show, state_store);
static struct kobj_attribute color_attr = __ATTR(color, 0644, color_show, color_store);
static struct attribute *led_attrs[] = {
&state_attr.attr,
&color_attr.attr,
NULL,
};
static struct attribute_group led_attr_group = {
.attrs = led_attrs,
};
static struct kobj_type led_ktype = {
.release = led_release,
.sysfs_ops = &kobj_sysfs_ops,
.default_attrs = led_attrs, /* 4.1.15 用 default_attrs;default_groups 为 4.9+ */
};
static void led_release(struct kobject *kobj)
{
printk("led kobj released\n");
}
static int __init sysfs_init(void)
{
my_kobj = kobject_create_and_add("my_led", kernel_kobj);
if (!my_kobj)
return -ENOMEM;
my_kobj->ktype = &led_ktype;
printk("sysfs example loaded\n");
printk("Try: cat /sys/kernel/my_led/state\n");
printk("Try: echo 1 > /sys/kernel/my_led/state\n");
return 0;
}
static void __exit sysfs_exit(void)
{
kobject_put(my_kobj);
printk("sysfs example unloaded\n");
}
module_init(sysfs_init);
module_exit(sysfs_exit);
MODULE_LICENSE("GPL");
测试命令:
# 加载模块
insmod sysfs_example.ko
# 查看属性
cat /sys/kernel/my_led/state
cat /sys/kernel/my_led/color
# 设置属性
echo 1 > /sys/kernel/my_led/state
echo "green" > /sys/kernel/my_led/color
# 查看内核消息
dmesg | tail
# 卸载模块
rmmod sysfs_example
kobject创建 sysfs路径生成
───────────── ──────────────
kobject_init_and_add( /sys/
&dev->kobj, └── kernel/
&my_ktype, └── my_led/
kernel_kobj, ├── state
"my_led") ├── color
└── subsystem -> /sys/bus/platform
kset_create_and_add( /sys/
"my_devices", └── my_devices/
&my_uevent_ops, ├── sensor0/
NULL) ├── name
├── id
└── subsystem -> /sys/my_devices
/* 内核源码: include/linux/device.h(Linux 4.1.15;device/bus.h 是 5.0+ 才拆分出来的)*/
struct bus_type {
const char *name; /* 总线名称 */
const char *dev_name; /* 设备名称前缀 */
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 (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
const struct dev_pm_ops *pm; /* 电源管理操作集(指针,不是函数)*/
struct subsys_private *p; /* 总线私有数据 */
};
⚠️ 易错点:
struct bus_type没有struct module *owner字段;pm是const struct dev_pm_ops *指针;p的真实类型是struct subsys_private *。
/* 内核源码: include/linux/device.h(Linux 4.1.15) */
struct device {
struct device *parent; /* 父设备 */
struct device_private *p;
struct kobject kobj; /* 内嵌kobject */
const char *init_name; /* 初始名称 */
const struct device_type *type; /* 设备类型 */
struct bus_type *bus; /* 所属总线 */
struct device_driver *driver; /* 绑定的驱动 */
void *platform_data; /* 平台数据 */
void *driver_data; /* 驱动私有数据 */
struct dev_pm_info power; /* 电源管理 */
struct dev_pm_domain *pm_domain; /* 电源域 */
struct device_node *of_node; /* OF设备树节点 */
struct attribute_group **groups;
struct class *class; /* 所属类 */
void (*release)(struct device *dev); /* 释放函数 */
};
⚠️ 易错点:
struct device没有resource/num_resources字段——它们属于struct platform_device;4.1.15 的struct device也没有struct acpi_dev_node *acpi_node指针(仅 ARM/ACPI 平台才涉及,且类型不同)。
/* 内核源码: include/linux/device.h(Linux 4.1.15;device/driver.h 是 5.0+ 才拆分出来的)*/
struct device_driver {
const char *name; /* 驱动名称 */
struct bus_type *bus; /* 所属总线 */
struct module *owner;
const struct of_device_id *of_match_table; /* OF匹配表 */
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 dev_pm_ops *pm; /* 电源管理操作集(指针,不是函数)*/
struct driver_private *p;
};
⚠️ 易错点:
pm在 4.1.15 是const struct dev_pm_ops *,而不是int (*pm)(...)函数指针。
设备注册流程:
┌─────────────┐ ┌─────────────────┐ ┌──────────────────┐
│ device_add() │───→│ bus_add_device() │───→│ bus_probe_device()│
└─────────────┘ └─────────────────┘ └──────────────────┘
│
↓
┌──────────────────┐
│bus_for_each_drv() │
└──────────────────┘
│
↓
┌──────────────────┐
│driver_match_device│
└──────────────────┘
│
↓
┌──────────────────┐
│driver_probe_device│
└──────────────────┘
│
↓
┌──────────────────┐
│ drv->probe(dev) │
└──────────────────┘
驱动注册流程:
┌──────────────────┐ ┌─────────────────┐ ┌──────────────────┐
│ driver_register() │───→│ bus_add_driver() │───→│ driver_attach() │
└──────────────────┘ └─────────────────┘ └──────────────────┘
│
↓
┌──────────────────┐
│bus_for_each_dev() │
└──────────────────┘
│
↓
┌──────────────────┐
│__driver_attach() │
└──────────────────┘
/* platform总线的match函数 */
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
/* 0. driver_override 指定了驱动名时只匹配它(4.1 已存在) */
if (pdev->driver_override)
return !strcmp(pdev->driver_override, drv->name);
/* 1. 先尝试OF匹配(设备树) */
if (of_driver_match_device(dev, drv))
return 1;
/* 2. 尝试ACPI匹配 */
if (acpi_driver_match_device(dev, drv))
return 1;
/* 3. 尝试ID表匹配 */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
/* 4. 尝试名称匹配(最后手段) */
return (strcmp(pdev->name, drv->name) == 0);
}
/* I2C总线的match函数 */
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_driver *driver = to_i2c_driver(drv);
/* 1. 先尝试OF匹配 */
if (of_driver_match_device(dev, drv))
return 1;
/* 2. 尝试ACPI匹配 */
if (acpi_driver_match_device(dev, drv))
return 1;
/* 3. 尝试ID表匹配 */
if (driver->id_table)
return i2c_match_id(driver->id_table, client) != NULL;
/* 4. 尝试名称匹配 */
return (strcmp(client->name, drv->name) == 0);
}
⚠️ 版本差异:Linux 4.1.15 的
i2c_device_match()在 id_table 未命中时直接return 0(不做名称比较);上例第 4 步的"名称匹配"并非 4.1.15 的行为(platform_match才有名称回退)。
/* 注册总线 */
int bus_register(struct bus_type *bus);
/* 注销总线 */
void bus_unregister(struct bus_type *bus);
/* 添加设备到总线 */
int bus_add_device(struct device *dev);
/* 从总线移除设备 */
void bus_remove_device(struct device *dev);
/* 添加驱动到总线 */
int bus_add_driver(struct device_driver *drv);
/* 从总线移除驱动 */
void bus_remove_driver(struct device_driver *drv);
/* 遍历总线上的所有设备 */
int bus_for_each_dev(struct bus_type *bus, struct device *start,
void *data, int (*fn)(struct device *, void *));
/* 遍历总线上的所有驱动 */
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
void *data, int (*fn)(struct device_driver *, void *));
/* 内核源码: include/linux/platform_device.h(Linux 4.1.15) */
struct platform_device {
const char *name; /* 设备名称 */
int id; /* 设备ID,-1表示自动分配 */
bool id_auto;
struct device dev; /* 内嵌device */
u32 num_resources;
struct resource *resource; /* 资源数组 */
const struct platform_device_id *id_entry;
char *driver_override; /* 强制匹配指定驱动 */
struct mfd_cell *mfd_cell;
struct pdev_archdata archdata;
};
⚠️ 易错点:
platform_device没有of_node/acpi_node/platform_data/driver_data字段,它们都属于内嵌的dev(如pdev->dev.of_node、pdev->dev.platform_data)。
/* 资源结构 */
struct resource {
resource_size_t start; /* 起始地址 */
resource_size_t end; /* 结束地址 */
const char *name; /* 资源名称 */
unsigned long flags; /* 资源标志 */
struct list_head list;
struct resource *parent, *sibling, *child;
};
/* 资源标志 */
#define IORESOURCE_IO 0x00000100 /* IO端口资源 */
#define IORESOURCE_MEM 0x00000200 /* 内存资源 */
#define IORESOURCE_IRQ 0x00000400 /* IRQ资源 */
#define IORESOURCE_DMA 0x00000800 /* DMA资源 */
/* 内核源码: include/linux/platform_device.h */
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
bool prevent_deferred_probe;
};
/* ID表 */
struct platform_device_id {
char name[PLATFORM_NAME_SIZE];
kernel_ulong_t driver_data;
};
/* 注册platform设备 */
int platform_device_register(struct platform_device *pdev);
void platform_device_unregister(struct platform_device *pdev);
/* 注册platform驱动 */
int platform_driver_register(struct platform_driver *drv);
void platform_driver_unregister(struct platform_driver *drv);
/* 获取资源 */
struct resource *platform_get_resource(struct platform_device *pdev,
unsigned int type, unsigned int num);
int platform_get_irq(struct platform_device *pdev, unsigned int num);
struct resource *platform_get_resource_byname(struct platform_device *pdev,
unsigned int type, const char *name);
int platform_get_irq_byname(struct platform_device *pdev, const char *name);
/* 访问平台数据 */
static inline void *platform_get_drvdata(struct platform_device *pdev)
{
return dev_get_drvdata(&pdev->dev);
}
static inline void platform_set_drvdata(struct platform_device *pdev, void *data)
{
dev_set_drvdata(&pdev->dev, data);
}
platform设备注册流程:
1. platform_device_register(pdev)
│
├──→ platform_device_add(pdev)
│ │
│ ├──→ pdev->dev.bus = &platform_bus_type // 设置总线
│ │
│ ├──→ of_device_add(pdev) // 设备树设备
│ │ └──→ of_platform_device_create_pdata()
│ │
│ └──→ platform_device_add_resources(pdev) // 添加资源
│
└──→ device_add(&pdev->dev) // 添加到设备模型
└──→ bus_probe_device(dev) // 尝试匹配驱动
└──→ device_attach(dev)
└──→ bus_for_each_drv(...)
└──→ __driver_attach(drv, dev)
└──→ driver_match_device(drv, dev)
└──→ drv->bus->match(dev, drv)
└──→ driver_probe_device(drv, dev)
└──→ drv->probe(dev)
└──→ pdrv->probe(pdev)
platform驱动注册流程:
1. platform_driver_register(pdrv)
│
├──→ driver_register(&pdrv->driver)
│ │
│ └──→ bus_add_driver(drv)
│ │
│ └──→ driver_attach(drv)
│ └──→ bus_for_each_dev(...)
│ └──→ __driver_attach(drv, dev)
│ └──→ driver_match_device(drv, dev)
│ └──→ driver_probe_device(drv, dev)
│
└──→ pdrv->driver.bus = &platform_bus_type
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
/* 设备私有数据 */
struct my_plat_data {
int gpio_num;
const char *label;
struct mutex lock;
};
/* 探测函数 */
static int my_plat_probe(struct platform_device *pdev)
{
struct my_plat_data *priv;
struct device_node *np = pdev->dev.of_node;
enum of_gpio_flags flags;
int ret;
printk("Probing device: %s\n", pdev->name);
/* 分配私有数据 */
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
/* 从设备树获取GPIO */
priv->gpio_num = of_get_named_gpio_flags(np, "led-gpio", 0, &flags);
if (priv->gpio_num < 0) {
dev_err(&pdev->dev, "Failed to get GPIO\n");
return priv->gpio_num;
}
/* 申请GPIO */
ret = devm_gpio_request(&pdev->dev, priv->gpio_num, "my_led");
if (ret) {
dev_err(&pdev->dev, "Failed to request GPIO\n");
return ret;
}
/* 设置GPIO方向 */
ret = gpio_direction_output(priv->gpio_num, 0);
if (ret) {
dev_err(&pdev->dev, "Failed to set GPIO direction\n");
return ret;
}
/* 获取标签 */
of_property_read_string(np, "label", &priv->label);
mutex_init(&priv->lock);
platform_set_drvdata(pdev, priv);
gpio_set_value(priv->gpio_num, 1);
dev_info(&pdev->dev, "Device probed, label: %s\n", priv->label);
return 0;
}
/* 移除函数 */
static int my_plat_remove(struct platform_device *pdev)
{
struct my_plat_data *priv = platform_get_drvdata(pdev);
gpio_set_value(priv->gpio_num, 0);
dev_info(&pdev->dev, "Device removed\n");
return 0;
}
/* 设备树匹配表 */
static const struct of_device_id my_plat_of_match[] = {
{ .compatible = "mycompany,my-led" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_plat_of_match);
/* 平台ID表 */
static const struct platform_device_id my_plat_id_table[] = {
{ "my_led", 0 },
{ /* sentinel */ }
};
/* platform驱动结构体 */
static struct platform_driver my_plat_driver = {
.probe = my_plat_probe,
.remove = my_plat_remove,
.driver = {
.name = "my_led",
.of_match_table = my_plat_of_match,
},
.id_table = my_plat_id_table,
};
module_platform_driver(my_plat_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("嵌入式Linux");
对应的设备树节点:
/* IMX6ULL */
my_led@0209c000 {
compatible = "mycompany,my-led";
reg = <0x0209c000 0x1000>;
led-gpio = <&gpio5 3 GPIO_ACTIVE_HIGH>;
label = "green-led";
status = "okay";
};
| 特性 | IMX6ULL | STM32 (Linux) | RK3568 |
|---|---|---|---|
| 总线 | platform, i2c, spi | platform, i2c, spi | platform, i2c, spi, pcie |
| 设备树 | 必需 | 支持(也支持ACPI) | 必需 |
| GPIO控制器 | gpio-mxs | gpio-stm32 | gpio-rk3568 |
| 时钟控制器 | clk-imx6q | clk-stm32mp1 | clk-rk3568 |
| pinctrl | pinctrl-imx | pinctrl-stm32 | pinctrl-rockchip |
| 中断控制器 | gic | nvic/gic | gic-v3 |
/* IMX6ULL - UART1 */
&uart1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart1>;
status = "okay";
};
/* STM32MP1 - UART4 */
&uart4 {
pinctrl-names = "default";
pinctrl-0 = <&uart4_pins_b>;
status = "okay";
};
/* RK3568 - UART2 */
&uart2 {
pinctrl-names = "default";
pinctrl-0 = <&uart2m1_xfer>;
status = "okay";
};
┌─────────────────────────────────────────────────────────────┐
│ Linux设备模型通用性 │
│ │
│ 所有平台共享: │
│ ├── kobject / kset / ktype ← 基础对象模型 │
│ ├── sysfs (/sys) ← 用户空间接口 │
│ ├── bus_type ← 总线抽象层 │
│ ├── device / device_driver ← 设备与驱动 │
│ └── platform_bus ← 平台总线 │
│ │
│ 平台差异仅在: │
│ ├── 设备树描述 ← 硬件拓扑 │
│ ├── 总线驱动实现 ← 底层寄存器操作 │
│ └── 时钟/电源/Pinctrl配置 ← SoC特定硬件 │
└─────────────────────────────────────────────────────────────┘
考察点: kobject生命周期、引用计数
参考答案:
kobject生命周期包含5个状态:
kzalloc()分配内存后,kobject处于未初始化状态kobject_init()或kobject_init_and_add()后,引用计数设为1kobject_add()后,kobject在sysfs中可见kobject_del()后,从sysfs移除但内存仍在kobject_put()使引用计数为0时,触发release()函数释放内存关键规则:
kobject_init()后引用计数为1kobject_add()不改变引用计数kobject_get()引用计数+1kobject_put()引用计数-1ktype->release()释放内存考察点: kset、ktype、kobject关系
参考答案:
kset:同类型kobject的容器
uevent_ops控制热插拔事件/sys/<kset_name>/ktype:定义kobject的类型行为
release:释放函数,引用计数为0时调用sysfs_ops:定义show/store函数default_attrs:默认属性文件关系:kobject是基础对象,kset是容器,ktype是类型描述。一个kobject必须有一个ktype,可以属于一个kset,也可以不属于任何kset。
考察点: sysfs接口、用户空间通信
参考答案:
show函数:
ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf);
buf:内核分配的缓冲区,最大PAGE_SIZE-1字节(通常4095字节)store函数:
ssize_t store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count);
buf:用户空间写入的数据,注意末尾可能有换行符count:用户写入的字节数注意事项:
sysfs_emit()替代sprintf()(内核5.10+)kstrtoint()等安全转换函数考察点: platform总线、设备匹配
参考答案:
platform总线的match函数按以下优先级匹配:
OF匹配(设备树):of_driver_match_device()检查compatible属性
static const struct of_device_id my_of_match[] = {
{ .compatible = "mycompany,my-device" },
};
ACPI匹配:acpi_driver_match_device()检查ACPI表
ID表匹配:检查platform_driver->id_table
static const struct platform_device_id my_id_table[] = {
{ "my_device", 0 },
};
名称匹配:最后手段,比较pdev->name和drv->name
现代Linux驱动开发推荐使用设备树匹配(优先级最高)。
考察点: platform驱动、设备树资源获取
参考答案:
static int my_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct resource *res;
int irq, gpio;
const char *label;
/* 1. 获取内存资源(寄存器地址) */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "Failed to get memory resource\n");
return -ENODEV;
}
/* 使用devm_ioremap_resource()映射寄存器 */
void __iomem *base = devm_ioremap_resource(&pdev->dev, res);
/* 2. 获取中断号 */
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
/* 3. 获取GPIO(通过设备树属性) */
gpio = of_get_named_gpio(np, "led-gpio", 0);
if (!gpio_is_valid(gpio))
return -EINVAL;
/* 4. 获取字符串属性 */
of_property_read_string(np, "label", &label);
/* 5. 获取u32属性 */
u32 value;
of_property_read_u32(np, "timeout", &value);
return 0;
}
关键API总结:
| 资源类型 | 获取API |
|---|---|
| 内存资源 | platform_get_resource(pdev, IORESOURCE_MEM, num) |
| 中断资源 | platform_get_irq(pdev, num) |
| GPIO | of_get_named_gpio(np, "name", index) |
| 字符串 | of_property_read_string(np, "name", &str) |
| u32 | of_property_read_u32(np, "name", &val) |
| 时钟 | devm_clk_get(&pdev->dev, "name") |
内容来源:设备模型(kobject/kset/kobj_type/sysfs、bus_type/device/device_driver、platform 总线)为内核源码级扩展知识,不属于《I.MX6U嵌入式Linux驱动开发指南》讲授范围,已按 Linux 4.1.15 源码(include/linux/kobject.h、include/linux/device.h、include/linux/platform_device.h、include/linux/sysfs.h)核对。属性文件、platform 驱动实战可参考原书「platform 设备驱动实验」「设备树下的 platform 驱动」等章节。
最后更新: 2026-09-17