Linux 2.6 前,裝置驅動缺乏統一架構,裝置拓撲關係分散在各子系統,用戶空間難以統一檢視。裝置模型 以 kobject 為核心,建立統一的物件層次樹,映射至 sysfs (/sys),實現:
uevent 通知 udev。struct kobject {
const char *name; // 物件名稱
struct list_head entry; // 掛載至 kset 列表
struct kobject *parent; // 父物件 (形成樹狀)
struct kset *kset; // 所屬集合
struct kobj_type *ktype; // 類型描述 (釋放、sysfs ops、預設屬性)
struct sysfs_dirent *sd; // sysfs 目錄項
struct kref kref; // 參考計數
unsigned int state_initialized:1;
unsigned int state_in_sysfs:1;
unsigned int state_add_uevent_sent:1;
unsigned int state_remove_uevent_sent:1;
unsigned int uevent_suppress:1;
};
關鍵欄位:
kref:kobject_get()/kobject_put() 管理生命週期,歸零觸發 ktype->release()。parent:構建樹狀層次,對應 /sys 目錄階層。kset:歸屬集合,決定在 /sys 中的父目錄位置。ktype:定義物件行為(釋放、屬性讀寫、預設屬性)。struct kobj_type {
void (*release)(struct kobject *kobj); // 釋放回調
const struct sysfs_ops *sysfs_ops; // 屬性讀寫操作
const struct attribute **default_attrs; // 預設屬性陣列
};
sysfs_ops 定義:
struct sysfs_ops {
ssize_t (*show)(struct kobject*, struct attribute*, char* buf); // 讀取屬性
ssize_t (*store)(struct kobject*, struct attribute*, const char*, size_t); // 寫入屬性
};
attribute 對應 /sys/.../屬性檔:
struct attribute {
const char *name; // 檔案名
struct module *owner; // 擁有模組
mode_t mode; // 權限 (如 0444 唯讀)
};
struct kset {
struct subsystem *subsys; // 所屬子系統 (舊架構殘留)
struct kobj_type *ktype; // 集合內物件共享類型
struct list_head list; // 成員 kobject 雙向鏈表
struct kobject kobj; // 內嵌 kobject (作為集合本身的物件)
struct kset_uevent_ops *uevent_ops;// 熱插拔事件操作
};
關鍵機制:
kobject->parent 指向內嵌 kobj,形成「集合目錄」語意。kset->list 串聯所有成員,kset_add() 執行 list_add_tail()。kset 本身也是 kobject,可嵌套形成多層集合。struct subsystem {
struct kset kset;
struct rw_semaphore rwsem; // 唯一差異:保護 list 的讀寫鎖
};
現代核心直接使用 kset,subsystem 僅為相容保留。
| 操作 | kobject | kset |
|---|---|---|
| 初始化 | kobject_init(kobj, ktype) | kset_init(kset) |
| 設定名稱 | kobject_set_name(kobj, fmt, ...) | 內嵌 kobj 同理 |
| 加入層次 | kobject_add(kobj, parent, fmt, ...) | kset_add(kset) |
| 註冊 (初始化+加入) | kobject_register(kobj) | kset_register(kset) |
| 取得參考 | kobject_get(kobj) | kset_get(kset) (實為內嵌 kobj) |
| 釋放參考 | kobject_put(kobj) | kset_put(kset) |
| 移除層次 | kobject_del(kobj) | kset_del(kset) |
| 註銷 (移除+釋放) | kobject_unregister(kobj) | kset_unregister(kset) |
kobject_add() 時,根據 parent 或 kset->kobj 決定在 /sys 下建立目錄。kobject->name。default_attrs 中的每個 attribute 在目錄下建立同名檔案,讀寫分別調用 sysfs_ops->show/store。uevent 事件:kobject_add() 觸發 add,kobject_del() 觸發 remove,kset_uevent_ops 可自訂環境變數。/sys (sysfs 根目錄)
├── bus/ ← bus_type 子系統 (kset)
│ ├── usb/ ← usb_bus_type (kobject 在 bus kset 內)
│ │ ├── devices/ ← usb 裝置 kset
│ │ │ ├── 1-1/ ← usb 裝置 kobject
│ │ │ └── 1-2/
│ │ └── drivers/ ← usb 驅動 kset
│ │ ├── usb-storage/
│ │ └── usbhid/
│ └── pci/ ← pci_bus_type
├── class/ ← class 子系統
│ ├── block/ ← 區塊裝置類別
│ ├── net/ ← 網路介面類別
│ └── tty/ ← 終端類別
├── devices/ ← 全域裝置拓撲樹 (platform_bus 等)
└── module/ ← 載入模組 kset
kobject **從不單獨存在**,而是嵌入業務結構體:
struct cdev { // 字元裝置
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
dev_t dev;
// ...
};
struct device { // 通用裝置
struct kobject kobj;
struct device *parent;
struct bus_type *bus;
struct device_driver *driver;
// ...
};
struct device_driver { // 驅動
struct kobject kobj;
struct bus_type *bus;
// ...
};
取得容器指標:container_of(kobj_ptr, container_type, kobj_field_name)。
// 1. 定義屬性
static struct kobj_attribute foo_attr = __ATTR_RW(foo); // 0644 權限
// 2. 定義屬性群組
static struct attribute *attrs[] = { &foo_attr.attr, NULL };
static struct attribute_group attr_group = { .attrs = attrs };
// 3. 定義 ktype
static struct kobj_type my_ktype = {
.release = my_release,
.sysfs_ops = &my_sysfs_ops,
.default_attrs = attrs,
};
// 4. 模組初始化建立 kobject
static struct kobject *my_kobj;
int __init my_init(void) {
my_kobj = kobject_create_and_add("my_device", kernel_kobj); // 掛在 /sys/kernel/my_device
if (!my_kobj) return -ENOMEM;
return sysfs_create_group(my_kobj, &attr_group); // 建立屬性檔
}
void __exit my_exit(void) {
kobject_put(my_kobj); // 觸發 release
}
kobject/kset/ktype 是 Linux 裝置模型的「原子」、「分子」、「化學式」。理解它們如何構建樹狀層次、映射至 sysfs、管理生命週期與熱插拔,是深入核心驅動開發、分析裝置拓撲、編寫 udev 規則的必經之路。