1.kobject, ktype, kset
kobject代表sysfs中的目錄。
ktype代表kobject的類型,主要包含release函數和attr的讀寫函數。比如,所有的bus都有同一個bus_type;所有的class都有同一個class_type。
kset包含了subsystem概念,kset本身也是一個kobject,所以裡麵包含了一個kobject對象。另外,kset中包含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);
這三個函數都與uevent相關。filter用於判斷uevent是否要發出去。name用於得到subsystem的名字。uevent用於填充env變數。
2.uevent核心部分
uevent是sysfs向使用者空間發出的訊息。比如,device_add函數中,會調用kobject_uevent(&dev->kobj, KOBJ_ADD); 這裡kobj是發訊息的kobj,KOBJ_ADD是發出的事件。uevent的事件在kobject_action中定義:
enum kobject_action {
KOBJ_ADD,
KOBJ_REMOVE,
KOBJ_CHANGE,
KOBJ_MOVE,
KOBJ_ONLINE,
KOBJ_OFFLINE,
KOBJ_MAX
};
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
{
return kobject_uevent_env(kobj, action, NULL);
}
kobject_uevent_env:
由kobject的parent向上尋找,直到找到一個kobject包含kset。
如果kset中有filter函數,調用filter函數,看看是否需要過濾uevent訊息。
如果kset中有name函數,調用name函數得到subsystem的名字;否則,subsystem的名字是kset中kobject的名字。
分配一個kobj_uevent_env,並開始填充env環境變數:
增加環境變數ACTION=<action name>
增加環境變數DEVPATH=<kobj’s path>
增加環境變數SUBSYSTEM=<subsystem name>
增加環境變數kobject_uevent_env中參數envp_ext指定的環境變數。
調用kset的uevent函數,這個函數會繼續填充環境變數。
增加環境變數SEQNUM=<seq>,這裡seq是靜態變數,每次累加。
調用netlink發送uevent訊息。
調用uevent_helper,最終轉換成對使用者空間sbin/mdev的調用。
3.uevent使用者空間部分
uevent的使用者空間程式有兩個,一個是udev,一個是mdev。
udev通過netlink監聽uevent訊息,它能完成兩個功能:
1.自動載入模組
2.根據uevent訊息在dev目錄下添加、刪除裝置節點。
另一個是mdev,mdev在busybox的程式碼封裝中能找到,它通過上節提到的uevent_helper函數被調用。
下面簡要介紹udev的模組自動載入過程:
etc目錄下有一個uevent規則檔案/etc/udev/rules.d/50-udev.rules
udev程式收到uevent訊息後,在這個規則檔案裡匹配,如果匹配成功,則執行這個匹配定義的shell命令。例如,規則檔案裡有這麼一行:
ACTION=="add", SUBSYSTEM=="?*", ENV{MODALIAS}=="?*", RUN+="/sbin/modprobe $env{MODALIAS}"
所以,當收到uevent的add事件後,shell能自動載入在MODALIAS中定義的模組。
mdev的模組自動載入過程與之類似,它的設定檔在/etc/mdev.conf中。例如:
$MODALIAS=.* 0:0 660 @modprobe "$MODALIAS"
這條規則指的是:當收到的環境變數中含有MODALIAS,那麼載入MODALIAS代表的模組。
mdev的詳細說明在busybox的docs/mdev.txt中。
4.uevent在裝置驅動模型中的應用
在sys目錄下有一個子目錄devices,代表一個kset。
建立裝置時,調用的device_initialize函數中,預設會把kset設定成devices_kset,即devices子目錄代表的kset。
devices_kset中設定了uevent操作集device_uevent_ops。
static struct kset_uevent_ops device_uevent_ops = {
.filter = dev_uevent_filter,
.name = dev_uevent_name,
.uevent = dev_uevent,
};
dev_uevent_filter中,主要是規定了要想發送uevent,dev必須有class或者bus。
dev_uevent_name中,返回dev的class或者bus的名字。
dev_uevent函數:
如果dev有裝置號,添加環境變數MAJOR與MINOR。
如果dev->type有值,設定DEVTYPE=<dev->type->name>。
如果dev->driver,設定DRIVER=<dev->driver->name>。
如果有bus,調用bus的uevent函數。
如果有class,調用class的uevent函數。
如果有dev->type,調用dev->type->uevent函數。
一般在bus的uevent函數中,都會添加MODALIAS環境變數,設定成dev的名字。這樣,uevent傳到使用者空間後,就可以通過對MODALIAS的匹配自動載入模組。這樣的bus例子有platform和I2C等等。