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); //這個函數放置在指定區域,初始化執行