設備模型四(uevent)

  • 前言
    什麼叫熱插拔呢? 打個比方, 插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

發佈了60 篇原創文章 · 獲贊 46 · 訪問量 21萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章