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等等。