天天看點

kobject之kobject_uevent.c檔案分析

struct      kset_uevent_ops {

       int                 (*filter)(struct kset *kset, struct kobject *kobj);          //過濾函數,kset中的kobj是否需要處理

       const char      *(*name)(struct kset *kset, struct kobject *kobj);        //傳回name

       int                 (*uevent)(struct kset *kset, struct kobject *kobj,         //uevent函數

                    struct kobj_uevent_env *env);

};

struct      kobj_uevent_env {

       char       *envp[UEVENT_NUM_ENVP];         //buf中每一段字元串的開始位址

       int          envp_idx;                                          //有幾個envp,或者說buf中有幾段字元串

       char       buf[UEVENT_BUFFER_SIZE];          //緩存(可以儲存好幾段字元串)

       int          buflen;                                              //buf尾

};

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

int   kobject_uevent_env(struct kobject *kobj, enum kobject_action action, char *envp_ext[])

調用kset-> uevent_ops-> uevent()函數

int   add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)

将格式化資訊儲存進env->buf中,關于env結構體可參考kobject.c的檔案分析開頭

int   kobject_action_type(const char *buf, size_t count,      enum kobject_action *type)

查找buf屬于哪一個action動作,屬于的動作傳回到type中

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

由于kref.c代碼很少,是以就把這兩個檔案合在一起分析了

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//原子的操作refcount,将其設定為1

void       kref_init(struct kref *kref)

{

       atomic_set(&kref->refcount,1);

       smp_mb();

}

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//計數加1

void       kref_get(struct kref *kref)

{

       atomic_inc(&kref->refcount);

       smp_mb__after_atomic_inc();

}

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//計數減1,為0則調用release函數

int   kref_put(struct kref *kref,    void (*release)(struct kref *kref))

{

       if (atomic_dec_and_test(&kref->refcount)) {

              release(kref);

              return 1;

       }

       return 0;

}

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//關于uevent,目前猜測他應該是和熱插拔有關

u64        uevent_seqnum;

char       uevent_helper[UEVENT_HELPER_PATH_LEN] =           //256

       CONFIG_UEVENT_HELPER_PATH;        // "/sbin/hotplug",這個定義在autoconf中

static      DEFINE_SPINLOCK(sequence_lock);

static      struct sock     *uevent_sock;

//這幾個狀态都是定義在kobject.h的枚舉結構之中

static const char     *kobject_actions[] = {

       [KOBJ_ADD] =                   "add",

       [KOBJ_REMOVE] =           "remove",

       [KOBJ_CHANGE] =            "change",

       [KOBJ_MOVE] =                "move",

       [KOBJ_ONLINE] =             "online",

       [KOBJ_OFFLINE] =            "offline",

};

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

跳讀一下,在分析kobject.c檔案時,我們看得最多的是kobject_uevent和kobject_uevent_env函數,是以我們先來看看這兩個函數是幹什麼用的。

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//這個基本沒什麼說的了,直接調用了env

int   kobject_uevent(struct kobject *kobj,    enum kobject_action action)

{

       return     kobject_uevent_env(kobj, action, NULL);

}

EXPORT_SYMBOL_GPL(kobject_uevent);

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//好長一個函數啊,為了友善了解,我們先從kobject.c中拿出一個調用執行個體來比較

//在rename函數中有這樣的調用:

kobject_uevent_env(kobj, KOBJ_MOVE, envp);

其中envp的第一個元素儲存的是更名前的kobj路徑,第二個元素為空

sprintf(devpath_string, "DEVPATH_OLD=%s", devpath);

envp[0] = devpath_string;

envp[1] = NULL;

這個函數很長,但也是超級的繞,繞到最後,其實就一個核心操作:

retval = uevent_ops->uevent(kset, kobj, env);

其中kset為kobj的宿主kset, kobj的參數傳入,而env則是一些字元串資訊

int   kobject_uevent_env(

       struct kobject                      *kobj,                  //操作obj

       enum kobject_action     action,                   //動作( KOBJ_MOVE )

       char                            *envp_ext[])          //這個資訊也隻是添加進env->buf

{

       struct kobj_uevent_env *env;

       const char *action_string = kobject_actions[action];    //根據動作參數擷取字元串"move"

       const char *devpath = NULL;

       const char *subsystem;

       struct kobject *top_kobj;

       struct kset *kset;

       struct kset_uevent_ops *uevent_ops;

       u64 seq;

       int i = 0;

       int retval = 0;

       //--------------------------------------------------------------------------------------------

       //這一塊代碼的功能是找到kobj的宿主kset,然後從中提取出熱插拔的操作函數

       //--------------------------------------------------------------------------------------------

       //找到根kobj,或者關聯上了kset宿主

       top_kobj = kobj;

       while (!top_kobj->kset && top_kobj->parent)

              top_kobj = top_kobj->parent;

       //根obj沒有關聯kset,出錯

       if (!top_kobj->kset) {

              pr_debug("kobject attempted to send uevent without kset!\n");

              return -EINVAL;

       }

       //擷取宿主kset,并從kset中提取出熱插拔操作的ops

       kset = top_kobj->kset;

       uevent_ops = kset->uevent_ops;

       //--------------------------------------------------------------------------------------------

       //如果ops的filter()和name()函數有指定,則先執行他們

       //--------------------------------------------------------------------------------------------

       //如果ops存在 且 filter函數(過濾函數)有設定,則執行該函數

       //filter的作用應該是看看kobj是否被kset過濾到

       if (uevent_ops && uevent_ops->filter)

              if (!uevent_ops->filter(kset, kobj)) {

                     pr_debug("kobject filter function caused the event to drop!\n");

                     return 0;

              }

       //給subsystem指定name,如果ops->name()函數有指定,則從該函數傳回,否則就擷取kset->kobj的name。

       if (uevent_ops && uevent_ops->name)

              subsystem = uevent_ops->name(kset, kobj);

       else

              subsystem = kobject_name(&kset->kobj);

       //name為空,出錯傳回

       if (!subsystem) {

              pr_debug("unset subsystem caused the event to drop!\n");

              return 0;

       }

       //--------------------------------------------------------------------------------------------

       //申請一塊env結構體,并填充他的env->buf字元串資訊

       //--------------------------------------------------------------------------------------------

       //申請一快env記憶體

       env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);

       if (!env)

              return -ENOMEM;

       //擷取kobj的完整路徑

       devpath = kobject_get_path(kobj, GFP_KERNEL);

       if (!devpath) {

              retval = -ENOENT;

              goto exit;

       }

       //這裡出現了一個新的函數,add_uevent_var,OK,我們先中斷這裡,跳到後面去先分析這個函數

       //分析完add_uevent_var函數後,發現這個函數隻是将參數的字元串儲存進env->buf中,是以這一串函數隻是儲存一些字元串資訊

       retval = add_uevent_var(env, "ACTION=%s", action_string);    //動作名

       if (retval)

              goto exit;

       retval = add_uevent_var(env, "DEVPATH=%s", devpath);         //kobj路徑

       if (retval)

              goto exit;

       retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem); //subsys名

       if (retval)

              goto exit;

       //如果參數三不為空,再儲存參數三的字元串資訊

       if (envp_ext) {

              for (i = 0; envp_ext[i]; i++) {

                     retval = add_uevent_var(env, envp_ext[i]);

                     if (retval)

                            goto exit;

              }

       }

       //--------------------------------------------------------------------------------------------

       //最後執行ops->uevent函數

       //繞了個大圈,最後卻又調用了ops中指定的uevent

       if (uevent_ops && uevent_ops->uevent) {

              retval = uevent_ops->uevent(kset, kobj, env);

              if (retval) {

                     pr_debug ("%s - uevent() returned %d\n",

                              __FUNCTION__, retval);

                     goto exit;

              }

       }

       //将全局變量uevent_seqnum加1

       spin_lock(&sequence_lock);

       seq = ++uevent_seqnum;

       spin_unlock(&sequence_lock);

       retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);

       if (retval)

              goto exit;

       //幫助資訊,uevent_helper在申明時就已經給了一個預設的路徑

       //這一塊的代碼實際和這個函數的關聯不是很大了,是以可以不用理會

       if (uevent_helper[0]) {

              char *argv [3];

              argv [0] = uevent_helper;             // "/sbin/hotplug"

              argv [1] = (char *)subsystem;              // kset的name

              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;

              //讓核心空間的驅動程式啟用使用者空間的若幹應用程式,這個函數的簡介可參考這個網址:http://linux.chinaunix.net/techdoc/beginner/2009/05/05/1110318.shtml

              call_usermodehelper (argv[0], argv, env->envp, UMH_WAIT_EXEC);

       }

exit:

       kfree(devpath);             //釋放kobj路徑

       kfree(env);                   //釋放env

       return retval;

}

EXPORT_SYMBOL_GPL(kobject_uevent_env);

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//直接從上面抓他的一個引用來說問題:

retval = add_uevent_var(env, "ACTION=%s", action_string);

//分析完這個函數後,發現這個函數隻是将format中的字元儲存進env->buf中

int   add_uevent_var(

       struct kobj_uevent_env *env,

       const char *format, ...)

{

       va_list args;           //擷取格式化清單的參數

       int len;

       //idx大于等于envp指針數組的大小,出錯傳回

       if (env->envp_idx >= ARRAY_SIZE(env->envp)) {

              printk(KERN_ERR "add_uevent_var: too many keys\n");

              WARN_ON(1);

              return -ENOMEM;

       }

       //将格式化字元列印進env->buf中,

       //開始位址由env->buflen指定

       //最大長度由env->buf的大小 減去 env->buflen開始長度限制,以免越界

       va_start(args, format);

       len = vsnprintf(&env->buf[env->buflen],

                     sizeof(env->buf) - env->buflen,

                     format, args);

       va_end(args);

       //實際操作的長度在buf中越界了(實際是不可能的),出錯傳回

       if (len >= (sizeof(env->buf) - env->buflen)) {

              printk(KERN_ERR "add_uevent_var: buffer size too small\n");

              WARN_ON(1);

              return -ENOMEM;

       }

       //記錄本段字元串的開始位址

       env->envp[env->envp_idx++] = &env->buf[env->buflen];

       env->buflen += len + 1;

       return 0;

}

EXPORT_SYMBOL_GPL(add_uevent_var);

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//根據輸入的buf查找該buf屬于哪一個action指令

int   kobject_action_type(

       const char      *buf,                    //要處理的字元串

       size_t            count,                   //要處理的字元串的長度

       enum kobject_action     *type)     //傳回action類型

{

       enum kobject_action action;

       int ret = -EINVAL;

       //從buf中剔除最後一個回車換行字元

       if (count && buf[count-1] == '\n')

              count--;

       if (!count)

              goto out;

       for (action = 0; action < ARRAY_SIZE(kobject_actions); action++) {

              if (strncmp(kobject_actions[action], buf, count) != 0) //字元串不相等

                     continue;

              //長度不相等,以免将“a”,“ab”兩個誤比對

              if (kobject_actions[action][count] != '\0')           

                     continue;

              //找到比對的字元串

              *type = action;

              ret = 0;

              break;

       }

out:

       return ret;

}

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//建立一個sock,準備發送資訊到網絡上

static int __init      kobject_uevent_init(void)

{

       uevent_sock = netlink_kernel_create(&init_net, NETLINK_KOBJECT_UEVENT,

                                       1, NULL, NULL, THIS_MODULE);

       if (!uevent_sock) {

              printk(KERN_ERR

                     "kobject_uevent: unable to create netlink socket!\n");

              return -ENODEV;

       }

       return 0;

}

postcore_initcall(kobject_uevent_init);         //這個函數放置在指定區域,初始化執行