Linux統一裝置模型
簡介
Linux2.6核心提供了新的裝置模型,目的是為了對電腦上的所有裝置進行統一地表示和操作,包括裝置本身和裝置之間的串連關係。這個模型是在 分析了 PCI 和 USB 的匯流排驅動過程中得到的,這兩個匯流排類型能代表當前系統中的大多數裝置類型,它們都有完善的熱挺拔機制和電源管理的支援,也都有級連機制的支援,以橋接的 PCI/USB 匯流排控制器的方式可以支援更多的 PCI/USB 裝置。[2] 總體來說,Linux統一裝置模型具有以下的特點,
* 代碼重複最小化
* 提供諸如引用計數這樣的統一機制
* 可以列舉系統中所有的裝置,觀察它們的狀態,並且查看它們串連的匯流排
* 可以將系統中的全部裝置結構以樹的形式完整、有效展現出來-包括所有的匯流排和內部串連
* 可以將裝置和其對應的驅動聯絡起來,反之亦然
*可以將裝置按照類型加以歸類,比如分類為輸入裝置,而無需理解物理裝置的拓撲結構
*可以講裝置樹的葉子向其根的方向依次遍曆,以保證能以正確順序關閉各裝置的電源。
最後一點是實現裝置模型的最初動機。若想在核心中實現智能的電源管理,就需要來建立表示系統中裝置拓撲關係的樹結構[2]。如在一個典型的 PC 系統中,中央處理器(CPU)能直接控制的是 PCI 匯流排裝置,而 USB 匯流排裝置是以一個 PCI 裝置(PCI-USB橋)的形式接入在 PCI 匯流排裝置上,外部 USB 裝置再接入在 USB 匯流排裝置上;當電腦執行掛起(suspend)操作時, Linux 核心應該以 “外部USB裝置->USB匯流排裝置->PCI匯流排裝置” 的順序通知每一個裝置將電源掛起;執行恢複(resume)時則以相反的順序通知;反之如果不按此順序則將有裝置得不到正確的電源狀態變遷的通知,將無法正常工作[1]。
核心對象機制關鍵資料結構
kobject核心對象
Kobject是Linux 2.6引入的新的裝置管理機制,在核心中由struct kobject表示。通過這個資料結構使所有裝置在底層都具有統一的介面,kobject提供基本的對象管理,是構成Linux 2.6裝置模型的核心結構,它與sysfs檔案系統緊密關聯,每個在核心中註冊的kobject對象都對應於sysfs檔案系統中的一個目錄。
Kobject結構定義為:
struct kobject {
char * k_name; 指向裝置名稱的指標
char name[KOBJ_NAME_LEN]; 裝置名稱
struct kref kref; 對象引用計數
struct list_head entry; 掛接到所在kset中去的單元
struct kobject * parent; 指向父物件的指標
struct kset * kset; 所屬kset的指標
struct kobj_type * ktype; 指向其物件類型描述符的指標
struct dentry * dentry; sysfs檔案系統中與該對象對應的檔案節點路徑指標
};
其中的kref域表示該對象引用的計數,核心通過kref實現對象引用計數管理,核心提供兩個函數kobject_get()、kobject_put()分別用於增加和減少引用計數,當引用計數為0時,所有該對象使用的資源將被釋放。
Ktype 域是一個指向kobj_type結構的指標,表示該對象的類型。Kobj_type資料結構包含三個域:一個release方法用於釋放kobject占 用的資源;一個sysfs_ops指標指向sysfs動作表和一個sysfs檔案系統預設屬性列表。Sysfs動作表包括兩個函數store()和 show()。當使用者態讀取屬性時,show()函數被調用,該函數編碼指定屬性值存入buffer中返回給使用者態;而store()函數用於儲存使用者態 傳入的屬性值。
kset核心對象集合
Kobject通常通過kset組織成層次化的結構,kset是具有相同類型的kobject的集合,在核心中用kset資料結構表示,定義為:
struct kset {
struct subsystem * subsys; 所在的subsystem的指標
struct kobj_type * ktype; 指向該kset物件類型描述符的指標
struct list_head list; 用於串連該kset中所有kobject的鏈表頭
struct kobject kobj; 嵌入的kobject
struct kset_hotplug_ops * hotplug_ops; 指向熱插拔動作表的指標
};
包 含在kset中的所有kobject被組織成一個雙向迴圈鏈表,list域正是該鏈表的頭。Ktype域指向一個kobj_type結構,被該 kset中的所有kobject共用,表示這些對象的類型。Kset資料結構還內嵌了一個kobject對象(由kobj域表示),所有屬於這個kset 的kobject對象的parent域均指向這個內嵌的對象。此外,kset還依賴於kobj維護引用計數:kset的引用計數實際上就是內嵌的 kobject對象的引用計數。
subsystem核心對象子系統
Subsystem是一系列kset的集合,描述系統 中某一類裝置子系統,如block_subsys表示所有的塊裝置,對應於sysfs檔案系統中的block目錄。類似的,devices_subsys 對應於sysfs中的devices目錄,描述系統中所有的裝置。Subsystem由struct subsystem資料結構描述,定義為:
struct subsystem {
struct kset kset; 內嵌的kset對象
struct rw_semaphore rwsem; 互斥訪問訊號量
};
每 個kset必須屬於某個subsystem,通過設定kset結構中的subsys域指向指定的subsystem可以將一個kset加入到該 subsystem。所有掛接到同一subsystem的kset共用同一個rwsem訊號量,用於同步訪問kset中的鏈表。
Linux統一裝置模型基本結構
| 類型 |
所包含的內容 |
對應核心資料結構 |
對應/sys項 |
| 裝置(Devices) |
裝置是此模型中最基本的類型,以裝置本身的串連按層次組織 |
struct device |
/sys/devices/*/*/.../ |
| 裝置驅動(Device Drivers) |
在一個系統中安裝多個相同裝置,只需要一份驅動程式的支援 |
struct device_driver |
/sys/bus/pci/drivers/*/ |
| 匯流排類型(Bus Types) |
在整個匯流排層級對此匯流排上串連的所有裝置進行管理 |
struct bus_type |
/sys/bus/*/ |
| 裝置類別(Device Classes) |
這是按照功能進行分類組織的裝置層次樹;如 USB 介面和 PS/2 介面的滑鼠都是輸入裝置,都會出現在 /sys/class/input/ 下 |
struct class |
/sys/class/*/ |
<linux/device.h> 中對於struct device的定義如下:
struct device {
struct klist klist_children;
struct klist_node knode_parent; /* node in sibling list */
struct klist_node knode_driver;
struct klist_node knode_bus;
struct device *parent;
struct kobject kobj;
char bus_id[BUS_ID_SIZE]; /* position on parent bus */
struct device_type *type;
unsigned is_registered:1;
unsigned uevent_suppress:1;
struct semaphore sem; /* semaphore to synchronize calls to
* its driver.
*/
struct bus_type *bus; /* type of bus device is on */
struct device_driver *driver; /* which driver has allocated this
device */
void *driver_data; /* data private to the driver */
void *platform_data; /* Platform specific data, device
core doesn't touch it */
struct dev_pm_info power;
#ifdef CONFIG_NUMA
int numa_node; /* NUMA node this device is close to */
#endif
u64 *dma_mask; /* dma mask (if dma'able device) */
u64 *dma_mask; /* dma mask (if dma'able device) */
u64 coherent_dma_mask;/* Like dma_mask, but for
alloc_coherent mappings as
not all hardware supports
64 bit addresses for consistent
allocations such descriptors. */
struct device_dma_parameters *dma_parms;
struct list_head dma_pools; /* dma pools (if dma'ble) */
struct dma_coherent_mem *dma_mem; /* internal for coherent mem
override */
/* arch specific additions */
struct dev_archdata archdata;
spinlock_t devres_lock;
struct list_head devres_head;
/* class_device migration path */
struct list_head node;
struct class *class;
dev_t devt; /* dev_t, creates the sysfs "dev" */
struct attribute_group **groups; /* optional groups */
void (*release)(struct device *dev);
};
其中比較重要的有:
struct device *parent;
struct kobject kobj;
struct bus_type *bus;
struct device_driver *driver;
struct class *class;
dev_t devt;
對於部分欄位,上面的struct定義裡面有解釋。
<linux/device.h>
struct device_driver {
const char *name;
struct bus_type *bus;
struct module *owner;
const char *mod_name; /* used for built-in modules */
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 attribute_group **groups;
struct driver_private *p;
};
device_driver的定義比較短,包含了name和bus等一些名稱,以及probe, remove, shutdown, suspend和resume這五個動作。
<linux/device.h>
struct bus_type {
const char *name;
struct bus_attribute *bus_attrs;
struct device_attribute *dev_attrs;
struct driver_attribute *drv_attrs;
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 (*suspend_late)(struct device *dev, pm_message_t state);
int (*resume_early)(struct device *dev);
int (*resume)(struct device *dev);
struct bus_type_private *p;
};
struct class {
const char *name;
struct module *owner;
struct kset subsys;
struct list_head children;
struct list_head devices;
struct list_head interfaces;
struct kset class_dirs;
struct semaphore sem; /* locks children, devices, interfaces */
struct class_attribute *class_attrs;
struct class_device_attribute *class_dev_attrs;
struct device_attribute *dev_attrs;
int (*uevent)(struct class_device *dev, struct kobj_uevent_env *env);
int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
void (*release)(struct class_device *dev);
void (*class_release)(struct class *class);
void (*dev_release)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
};
struct class定義了按照功能進行分類的標準,但是這個標準真正體現在/sys/class/裡面是通過struct class_device這個結構體進行的:
struct class_device {
struct list_head node;
struct kobject kobj;
struct class *class;
dev_t devt;
struct device *dev;
void *class_data;
struct class_device *parent;
struct attribute_group **groups;
void (*release)(struct class_device *dev);
int (*uevent)(struct class_device *dev, struct kobj_uevent_env *env);
char class_id[BUS_ID_SIZE];
};
SYS檔案系統
sysfs 是在這個 Linux 統一裝置模型的開發過程中的一項副產品,sysfs is a ram-based filesystem initially based on ramfs. It provides a means to export kernel data structures, their attributes, and the linkages between them to userspace
SYS檔案系統目錄:
sys: block, bus, , devices, firmware, fs, kernel, module, power 等
這些目錄展示了核心對各種裝置進行統一管理的模型和方式。
/sys/devices
這是核心對系統中所有裝置的分層次表達模型,也是 /sys 檔案系統管理裝置的最重要的目錄結構。
/sys/dev
這個目錄下維護一個按字元裝置和塊裝置的主次號碼(major:minor)連結到真實的裝置(/sys/devices下)的符號連結檔案。
/sys/bus
核心裝置按匯流排類型分層放置的目錄結構, devices 中的所有裝置都是串連於某種匯流排之下,在這裡的每一種具體匯流排之下可以找到每一個具體裝置的符號連結.
/sys/class
這是按照裝置功能分類的裝置模型,如系統所有輸入裝置都會出現在 /sys/class/input 之下,而不論它們是以何種匯流排串連到系統。
/sys/block deprecated
/sys/firmware
系統載入韌體機制的對使用者空間的介面
/sys/fs
這 裡按照設計是用於描述系統中所有檔案系統,包括檔案系統本身和按檔案系統分類存放的已掛載點,但目前只有 fuse,gfs2 等少數檔案系統支援 sysfs 介面,一些傳統的虛擬檔案系統(VFS)層次控制參數仍然在 sysctl (/proc/sys/fs) 介面中中;
/sys/kernel
這裡是核心所有可調整參數的位置,目前只有 uevent_helper, kexec_loaded, mm, 和新式的 slab 分配器等幾項較新的設計在使用它,其它核心可調整參數仍然位於 sysctl (/proc/sys/kernel) 介面中 ;
/sys/module
這裡有系統中所有模組的資訊,不論這些模組是以內聯(inlined)方式編譯到核心映像檔案(vmlinuz)中還是編譯為外部模組(ko檔案),都可能會出現在 /sys/module 中:
* 編譯為外部模組(ko檔案)在載入後會出現對應的 /sys/module/<module_name>/, 並且在這個目錄下會出現一些屬性檔案和屬性目錄來表示此外部模組的一些資訊,如版本號碼、載入狀態、所提供的驅動程式等;
* 編譯為內聯方式的模組則只在當它有非0屬性的模組參數時會出現對應的 /sys/module/<module_name>, 這些模組的可用參數會出現在 /sys/modules/<modname>/parameters/<param_name> 中,
o 如 /sys/module/printk/parameters/time 這個可讀寫參數控制著內聯模組 printk 在列印核心訊息時是否加上時間首碼;
o 所有內聯模組的參數也可以由 "<module_name>.<param_name>=<value>" 的形式寫在核心啟動參數上,如啟動核心時加上參數 "printk.time=1" 與 向 "/sys/module/printk/parameters/time" 寫入1的效果相同;
* 沒有非0屬性參數的內聯模組不會出現於此。
/sys/power
這裡是系統中電源選項,這個目錄下有幾個屬性檔案可以用於控制整個機器的電源狀態,如可以向其中寫入控制命令讓機器關機、重啟等。
參考文獻:
[1]陳莉君 Linux裝置驅動模型 part 1, part 2, part 3, part 4
[2]程任全 使用sys檔案系統訪問Linux 核心 full text
[3]http://blog.chinaunix.net/u2/86638/showart_1850276.html