前言
什麼叫熱插拔呢? 打個比方, 插U盤,對應的/dev/下有對應設備添加,拔出後,相應的設備被刪除,這個就叫熱插拔,即開機的狀態下,實現設備的即插即用。在設備模型中uevent這種事件機制支持此功能
在USB加入到系統中, 驅動會調用device_register會被調用, 此函數會發送消息給應用層,對應的udev收到了消息後就在/dev/下建立設備節點,這個消息的發送是熱插拔的關鍵,其實,是個函數kobject_uevent
下面討論下這個函數,老樣子,我們通過一個列子來講訴這個函數:
/sys/a/xx/attr
其中 a爲kset, xx爲kobj, xx對應的kset是a, xx對應的parent不是a, attr是xx的屬性文件kobject_uevent
kobject_uevent會調用kobject_uevent_env函數
top_kobj = kobj;//這裏調用kobject_uevent(kobj(xx), KOBJ_ADD)
while (!top_kobj->kset && top_kobj->parent)//從xx開始網上尋找
top_kobj = top_kobj->parent; //規則,順着xx開始找,尋找規則
//1.有kset就停
//2.沒有kset就順着parent網上找,所以這裏 xx有keset爲a,就停了,這裏的top_kobj就爲a
if (!top_kobj->kset) {
pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "
"without kset!\n", kobject_name(kobj), kobj,
__func__);
return -EINVAL;
}
kset = top_kobj->kset; //kset就是a
uevent_ops = kset->uevent_ops; //uevent_ops 就是a的kset_uevent_ops
往下走
//這裏爲xx的uevent_suppress 前面章節有提到過,當爲1時,則禁止uevent的發送
if (kobj->uevent_suppress) {
pr_debug("kobject: '%s' (%p): %s: uevent_suppress "
"caused the event to drop!\n",
kobject_name(kobj), kobj, __func__);
return 0;
}
之後
if (uevent_ops && uevent_ops->filter) //即a的kset_uevent_ops中的過濾函數 .filter
//一般是做判斷用,內核裏一般是用來判斷髮消息的類型是否符合xx的ktype
//比方說這個例子中,通過對attr的操作,使得xx要向上層發送消息,而通過kset發出,xx調用這個uevent函數,這個函數會調用所屬a中的.filter函數來判斷所發的消息是不是來自xx的,用於過濾一部分event
//比如說: /sys/a/xx /sys/a/yy 這裏xx與yy的kset都是a,這裏我在a的.filter方法實現的是判斷ktype是不是xx的,所以yy在發送event時是發不出的,這裏就是爲什麼叫.filter的原因
if (!uevent_ops->filter(kset, kobj)) {
pr_debug("kobject: '%s' (%p): %s: filter function "
"caused the event to drop!\n",
kobject_name(kobj), kobj, __func__);
return 0;
}
繼續
//添加了一些環境變量
retval = add_uevent_var(env, "ACTION=%s", action_string);
if (retval)
goto exit;
retval = add_uevent_var(env, "DEVPATH=%s", devpath);
if (retval)
goto exit;
retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
if (retval)
goto exit;
接着走
//這裏相當於調用a中的uevent函數,其實就是加點環境變量
//還是剛纔那個例子/sys/a/xx /sys/a/yy, 我想實現在xx,yy兩個kobj發消息的時候都要說明來自於哪裏的,所以可以再a中的uevent函數中實現,比如add_uevent_var(env, "from=%s", “a”);這樣的話無論xx或是yy發送消息的話,都會發送from這個環境變量
if (uevent_ops && uevent_ops->uevent) {
retval = uevent_ops->uevent(kset, kobj, env);
if (retval) {
pr_debug("kobject: '%s' (%p): %s: uevent() returned "
"%d\n", kobject_name(kobj), kobj,
__func__, retval);
goto exit;
}
}
最後
#if defined(CONFIG_NET)
/* send netlink message */
//如果定義這個宏,這個消息會通過socket機制發出去
//當然,現在一般都用這個,udev就是通過soket監聽的方式去工作
#endif
//緊接着就是hotplug機制,本質上就是啓動一個用戶進程,然後通過環境變量的方式傳給這個進程
uevent_helper[0]被初始化爲"/sbin/hotplug"
if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
char *argv [3];
argv [0] = uevent_helper;
argv [1] = (char *)subsystem;
argv [2] = NULL;
retval = add_uevent_var(env, "HOME=/");
if (retval)
goto exit;
retval = add_uevent_var(env,
"PATH=/sbin:/bin:/usr/sbin:/usr/bin");
if (retval)
goto exit;
retval = call_usermodehelper(argv[0], argv, //啓動進程
env->envp, UMH_WAIT_EXEC);
}
如果我們要喚醒hotplug機制, 需要echo “/sbin/hotplug”> /proc/sys/kernel/hotplug,之後將寫好的hotplug程序放在/sbin中
總結:
發送uevent密切相關的幾點
1.kobj的uevent_suppress
2.kobj所屬kset的 .filter與.uevent函數
在早期的linux版本中,一般採用hotplug機制來實現設備的熱插拔功能,但是,由於設備越來越多,在開機的時候, 發現一個設備就會調用一次用戶進行,即設備越多,調用進行的次數就越多,加大了系統的開銷,導致死機,所以現在一般採用netlink的方式通知設備工作, 在開機後,可以進行hotplug的小應用
當然,這個時候netlink與hotplug是同時有效的,還要注意的一點是, /proc/sys/kernel/hotplug在重啓後會自動清除,所以如果要用hotplug,每次開機都要echo “/sbin/hotplug”> /proc/sys/kernel/hotplug