このページの目次

デバイスモデル

カバー範囲: kobject/kset → device/driver/bus/class → sysfs バインディング → probe ライフサイクル → udev → デバイスツリー (DT) → ACPI → platform device カーネルバージョン: 2.6 ~ 6.x

概要

Linux デバイスモデルは、すべてのデバイス、ドライバ、バスに対して統一された表現とライフサイクル管理を提供するカーネル内部のフレームワークです。その真の価値は「デバイスがどのように動作するか」ではなく、「デバイスとドライバがどのように互いを見つけるか」、すなわちデバイス-ドライバのバインディング (device-driver binding) にあります。

kobject: 基本構成要素

// include/linux/kobject.h
struct kobject {
    const char      *name;           // sysfs 上に表示される名前
    struct kobject  *parent;         // 親ノード → sysfs ディレクトリ階層
    struct kset     *kset;           // 所属する kset (集合)
    struct kernfs_node *sd;         // sysfs 内の dentry (kernfs)
    struct kref     kref;            // リファレンスカウント
    unsigned int    state_initialized:1;
};

// kobject 自体はデバイスマネジメントを行いません。以下を提供します:
//   1. sysfs 内のディレクトリ/ファイル表現
//   2. リファレンスカウント → 自動解放
//   3. 親-子の階層関係

kset: kobject の集合

struct kset {
    struct list_head list;           // すべてのメンバ kobject
    struct kobject  kobj;            // 内包される kobject (集合自体が sysfs 上に存在)
    const struct kobj_type *ktype;   // 型操作
};

// 例: /sys/devices/ → devices_kset
//     /sys/bus/ → bus_kset

Device / Driver / Bus

struct device

// include/linux/device.h
struct device {
    struct kobject      kobj;               // sysfs 表現
    struct device       *parent;            // 親デバイス (例: PCI bridge)
    const char          *init_name;
    struct bus_type     *bus;               // 所属するバス
    struct device_driver *driver;           // バインドされたドライバ
    void                *driver_data;       // ドライバ固有データ
    struct device_node   *of_node;          // DT ノード
    u64                 *dma_mask;          // DMA アクセス可能範囲
    const struct attribute_group **groups;  // sysfs 属性グループ
    void (*release)(struct device *dev);    // 必須 (解放コールバック)
};

struct device_driver

struct device_driver {
    const char              *name;
    struct bus_type         *bus;
    int (*probe)(struct device *dev);           // プロブ: ドライバがデバイスにバインド
    void (*remove)(struct device *dev);         // リムーブ: アンバインド
    void (*shutdown)(struct device *dev);
    const struct of_device_id *of_match_table;  // DT マッチ
    const struct acpi_device_id *acpi_match_table;
    const struct attribute_group **groups;
};

struct bus_type

struct bus_type {
    const char      *name;                      // PCI, USB, platform, ...
    int (*match)(struct device *dev, struct device_driver *drv);
    int (*probe)(struct device *dev);           // バスレベルのプロブ
    struct subsys_private *p;                   // デバイスリスト + ドライバリスト
};

バインディングフロー

// drivers/base/dd.c
// デバイス登録:
device_register(dev)device_add(dev)bus_add_device(dev)     // bus->p->klist_devices にアタッチ
bus_probe_device(dev)device_initial_probe(dev)__device_attach(dev, true)bus_for_each_drv(dev->bus, ...)  // 登録済みドライバをすべて列挙
driver_match_device(drv, dev)  // bus->match() または of_match/acpi_match
device_driver_attach(drv, dev)driver_probe_device(drv, dev)really_probe(dev, drv)call_driver_probe(dev, drv) → drv->probe(dev)

重要⁠: バインディングは双方向です。新しいデバイスが現れた場合はドライバを列挙し、新しいドライバが登録された場合はデバイスを列挙します。どちらが先に登録されても、マッチングは発生します。


sysfs と属性

// /sys 下の各ディレクトリ = 1つの kobject
// 属性ファイル = struct device_attribute
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);
};

// マクロ: DEVICE_ATTR_RO(name) → 読み取り専用属性を生成
//      DEVICE_ATTR_WO(name) → 書き込み専用
//      DEVICE_ATTR_RW(name) → 読み書き両対応

Device Tree vs ACPI

Device Tree / ACPI / PCIe: ハードウェア記述方法は異なるが、マッチングメカニズムもそれぞれ異なる ARM/RISC-V · Device Tree ハードウェアトポロジを静的に記述 ブートローダからカーネルへ渡される platform_device が DT ノードにバインド of_match_table compatible 文字列でマッチング x86 · ACPI UEFI/BIOS によって提供 ファームウェアがハードウェアを動的に記述 + 電源管理メソッド acpi_match_table _HID / _CID でマッチング PCIe · 自己列挙 アーキテクチャを問わない DT / ACPI 記述に依存しない バス自身がデバイスを列挙 vendor:device ID pci_device_id でマッチング ハードウェア記述方法は異なるが、最終的な着地点は同じ: 各自の match_table に着地してデバイス-ドライバのマッチングを行う

udev

udev: カーネル uevent からデバイスノード/ルール適用までの処理チェーン カーネルがイベントを検知 uevent を発生 netlink ソケット カーネル → ユーザ空間へのチャネル udev デーモン uevent を受信 カーネルモジュールの読み込み modalias に基づく デバイスノードの作成 /dev/sda など カスタムルールの適用 権限 / シンボリックリンク カーネルは uevent を通知する役割のみを持ち、モジュールの読み込み、デバイスノードの作成、ルール適用はすべてユーザ空間の udev によって行われます

参考

  • ソースコード⁠: drivers/base/core.c, drivers/base/bus.c, drivers/base/dd.c, include/linux/device.h
  • カーネルドキュメント⁠: Documentation/driver-api/driver-model/

キーワード: kobject, device, driver, bus, probe, sysfs, device tree, ACPI, udev, modalias