06-设备模型与Kobject.md 50 KB


title: 设备模型与Kobject tags: [Linux内核, 设备模型, Kobject, sysfs, 嵌入式] created: 2026-09-16

updated: 2026-09-17

设备模型与Kobject

⚠️ 来源说明:本主题不属于《I.MX6U嵌入式Linux驱动开发指南》的讲授范围,内容基于 Linux 内核源码(Linux 4.1.15)整理,为扩展知识。

💡 关联知识: [[03-Linux驱动开发核心/07-platform驱动模型]] | [[03-Linux驱动开发核心/02-设备树语法与实战]] | [[02-嵌入式Linux内核基础/05-内存管理机制]]


一、设备模型概述

1.1 为什么需要设备模型

Linux内核在发展过程中,设备数量急剧增加,面临以下问题:

问题 描述
命名冲突 不同总线上的设备可能同名
设备发现 缺乏统一的设备枚举机制
电源管理 无法统一管理设备的休眠/唤醒
热插拔 无法动态响应设备的添加/移除
拓扑结构 无法表达设备间的层级关系

设备模型解决了这些问题,提供了:

  • 统一的设备层次结构:用树形结构表示设备关系
  • 统一的命名机制:通过总线区分同名设备
  • 统一的电源管理:父子设备的休眠/唤醒协调
  • 统一的热插拔支持:设备添加/移除时自动通知
  • 统一的sysfs接口:用户空间可查看设备信息

1.2 设备模型架构

┌─────────────────────────────────────────────────────────────┐
│                      用户空间                                │
├─────────────────────────────────────────────────────────────┤
│                      sysfs (/sys)                            │
├─────────────────────────────────────────────────────────────┤
│                    设备模型核心层                              │
│  ┌─────────┐   ┌─────────┐   ┌─────────┐   ┌─────────┐   │
│  │ kobject │   │  kset   │   │ ktype   │   │ 属性    │   │
│  └─────────┘   └─────────┘   └─────────┘   └─────────┘   │
├─────────────────────────────────────────────────────────────┤
│                    总线类型层                                 │
│  ┌─────────┐   ┌─────────┐   ┌─────────┐   ┌─────────┐   │
│  │  pci    │   │  usb    │   │ platform│   │  i2c    │   │
│  └─────────┘   └─────────┘   └─────────┘   └─────────┘   │
├─────────────────────────────────────────────────────────────┤
│                    设备/驱动层                                │
│  ┌─────────┐   ┌─────────┐   ┌─────────┐   ┌─────────┐   │
│  │ device  │   │ driver  │   │ class   │   │ bus     │   │
│  └─────────┘   └─────────┘   └─────────┘   └─────────┘   │
├─────────────────────────────────────────────────────────────┤
│                      硬件层                                  │
└─────────────────────────────────────────────────────────────┘

1.3 总线-设备-驱动模型

Linux设备模型的核心是总线-设备-驱动三元组:

总线 (bus)
├── 设备 (device)    ←→  匹配 (match)
└── 驱动 (driver)    ←→  匹配 (match)

工作流程:
1. 设备注册 → 总线查找匹配的驱动 → 调用驱动的probe()
2. 驱动注册 → 总线查找匹配的设备 → 调用驱动的probe()
组件 作用 关键结构体
总线 连接设备和驱动,负责匹配 struct bus_type
设备 描述硬件信息(地址、中断、时钟) struct device
驱动 实现设备操作(打开、读写、关闭) struct device_driver

二、Kobject详解

2.1 struct kobject

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 */
};

2.2 Kobject命名规则

/* 命名示例 */
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

2.3 kobject生命周期

kobject生命周期状态机:

[未初始化] ──kobject_init()──→ [已初始化] ──kobject_add()──→ [已添加]
     │                              │                              │
     │                              │                              │
     ↓                              ↓                              ↓
[销毁] ←──kobject_put()─── [释放] ←──kobject_del()─── [已删除]

2.4 kobject_init / kobject_add

/* 初始化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);

2.5 引用计数

⚠️ 来源说明:kobject 的生命周期与引用计数规则为内核源码级细节,不属于教材讲授范围,基于 Linux 4.1.15 源码(include/linux/kobject.hinclude/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));

引用计数规则

  1. kobject_init()后引用计数为1
  2. kobject_add()不增加引用计数
  3. kobject_get()增加引用计数
  4. kobject_put()减少引用计数
  5. 引用计数为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;
    }
    

2.6 Kobject使用示例

/* 完整的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与Ktype

3.1 struct kset

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);
};

3.2 Kset操作函数

/* 创建并初始化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);

3.3 struct kobj_type

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

3.4 关系图

┌─────────────────────────────────────────────────────────────┐
│                        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决定)  │
└─────────────────────────────────────────────────────────────┘

3.5 Kset使用示例

#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

4.1 sysfs挂载

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/          # 电源管理

4.2 设备属性

设备属性通过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

4.3 sysfs文件操作

/* 用户空间读取文件 → 内核调用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,
};

4.4 sysfs操作示例

#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

4.5 sysfs与kobject对应关系

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

五、总线、设备、驱动

5.1 struct bus_type

/* 内核源码: 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 字段;pmconst struct dev_pm_ops * 指针;p 的真实类型是 struct subsys_private *

5.2 struct device

/* 内核源码: 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 平台才涉及,且类型不同)。

5.3 struct device_driver

/* 内核源码: 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)(...) 函数指针。

5.4 匹配机制

设备注册流程:
┌─────────────┐    ┌─────────────────┐    ┌──────────────────┐
│ 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() │
                                        └──────────────────┘

5.5 匹配函数实现

/* 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 才有名称回退)。

5.6 总线注册与注销

/* 注册总线 */
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 *));

六、platform总线模型

6.1 platform_device

/* 内核源码: 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_nodepdev->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资源 */

6.2 platform_driver

/* 内核源码: 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;
};

6.3 注册与注销

/* 注册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);
}

6.4 匹配与探测流程

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

6.5 platform驱动示例

#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";
};

七、跨平台对比

7.1 IMX6ULL vs STM32 vs RK3568设备模型差异

特性 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

7.2 设备树示例对比

/* 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";
};

7.3 设备模型通用原则

┌─────────────────────────────────────────────────────────────┐
│                    Linux设备模型通用性                        │
│                                                             │
│  所有平台共享:                                              │
│  ├── kobject / kset / ktype     ← 基础对象模型              │
│  ├── sysfs (/sys)               ← 用户空间接口              │
│  ├── bus_type                   ← 总线抽象层                │
│  ├── device / device_driver     ← 设备与驱动                │
│  └── platform_bus               ← 平台总线                  │
│                                                             │
│  平台差异仅在:                                              │
│  ├── 设备树描述                    ← 硬件拓扑                │
│  ├── 总线驱动实现                  ← 底层寄存器操作          │
│  └── 时钟/电源/Pinctrl配置        ← SoC特定硬件             │
└─────────────────────────────────────────────────────────────┘

八、面试精选

题目1:请详细描述kobject的生命周期,包括引用计数如何管理对象的创建和销毁?

考察点: kobject生命周期、引用计数

参考答案

kobject生命周期包含5个状态:

  1. 未初始化:通过kzalloc()分配内存后,kobject处于未初始化状态
  2. 已初始化:调用kobject_init()kobject_init_and_add()后,引用计数设为1
  3. 已添加:调用kobject_add()后,kobject在sysfs中可见
  4. 已删除:调用kobject_del()后,从sysfs移除但内存仍在
  5. 释放/销毁:调用kobject_put()使引用计数为0时,触发release()函数释放内存

关键规则:

  • kobject_init()后引用计数为1
  • kobject_add()不改变引用计数
  • 每次kobject_get()引用计数+1
  • 每次kobject_put()引用计数-1
  • 引用计数为0时调用ktype->release()释放内存
  • 必须实现release函数,否则释放时会内存泄漏

题目2:kset和ktype的作用是什么?它们与kobject是什么关系?

考察点: kset、ktype、kobject关系

参考答案

kset:同类型kobject的容器

  • 内嵌一个kobject用于在sysfs中表示自身
  • 维护一个链表管理所有子kobject
  • 通过uevent_ops控制热插拔事件
  • sysfs路径:/sys/<kset_name>/

ktype:定义kobject的类型行为

  • release:释放函数,引用计数为0时调用
  • sysfs_ops:定义show/store函数
  • default_attrs:默认属性文件

关系:kobject是基础对象,kset是容器,ktype是类型描述。一个kobject必须有一个ktype,可以属于一个kset,也可以不属于任何kset。

题目3:sysfs中show/store函数的参数和返回值分别代表什么?有哪些注意事项?

考察点: sysfs接口、用户空间通信

参考答案

show函数

ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf);
  • buf:内核分配的缓冲区,最大PAGE_SIZE-1字节(通常4095字节)
  • 返回值:写入buf的字节数,或负数表示错误

store函数

ssize_t store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count);
  • buf:用户空间写入的数据,注意末尾可能有换行符
  • count:用户写入的字节数
  • 返回值:消耗的字节数,或负数表示错误

注意事项

  1. 使用sysfs_emit()替代sprintf()(内核5.10+)
  2. 使用kstrtoint()等安全转换函数
  3. store函数必须验证用户输入
  4. 属性文件权限设为0644(可读写)或0444(只读)
  5. 不要在show/store中执行耗时操作

题目4:platform总线的匹配机制是怎样的?优先级如何?

考察点: platform总线、设备匹配

参考答案

platform总线的match函数按以下优先级匹配:

  1. OF匹配(设备树)of_driver_match_device()检查compatible属性

    static const struct of_device_id my_of_match[] = {
       { .compatible = "mycompany,my-device" },
    };
    
  2. ACPI匹配acpi_driver_match_device()检查ACPI表

  3. ID表匹配:检查platform_driver->id_table

    static const struct platform_device_id my_id_table[] = {
       { "my_device", 0 },
    };
    
  4. 名称匹配:最后手段,比较pdev->namedrv->name

现代Linux驱动开发推荐使用设备树匹配(优先级最高)。

题目5:在编写platform驱动时,probe函数中如何获取设备树中的资源信息?请写出关键代码。

考察点: 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.hinclude/linux/device.hinclude/linux/platform_device.hinclude/linux/sysfs.h)核对。属性文件、platform 驱动实战可参考原书「platform 设备驱动实验」「设备树下的 platform 驱动」等章节。

最后更新: 2026-09-17