经过一段时间走走停停,终于对ktype,kobject,kset间的关系有所了解了,也接近尾声了。自学的乐趣在于无数次折腾中,用自己猜想反复去验证别人的思想,每一次小小的成就都是快乐,每一次看似合理的误解都隐藏着偌大的阴谋,终于千百度寻她,求得一个顺理成章的结果,当然也不是每次能成功。所以会想,世上有难事,只是少折腾,再笨的脑袋,只要愿意坚持也能上树,这里我们就暂时不讨论母猪的问题了。
kobj_type与kobject的关系比较简单,是一种明显的依存关系,正如价值因为人的存在而产生意义并发挥作用一样,kobj_type本身虽然包含了做什么(但未必包括怎么做,且听下文分析),但若没有了kobject,kobj_type也无法发挥作用,就像一条截断的手臂,它是真实存在那里,但却无法独立运动。同样地,这也证明了缺手缺角确实要比脑残好得多。用一种粗俗不求深刻的眼光去审视kobject_init_and_add源码,可以看到这么一句话kobject_init(kobj, ktype),正是媒婆kobject_init_and_add的这么一句话,kytpe嫁给了kobj,从此这个ktype只和这个kobj关联,当然离婚再嫁也是可以的。
继续漫无边际的分析、对比。以下是kset_create_and_add和kobject_init_and_add的主要流程图。当然,有人会问作者是不是脑残了,这些都是前面已经提过了的东西,是不是打算拆东墙补西墙,拼拼凑凑呢?那么,作者很可能会装出一副淡定哥的表情,直接无视。不废话了:
图、kobject_init_and_add与kset_create_and_add的主要流程图
注意到kobject_init_and_add中一个平凡而重要的函数kobject_add_internal在kset_create_and_add中再次出现了,这其实很容易解释,就是“你中有我,我中无你”的关系,或者说“你的就是我的,我的不是你的”这种关系,这里说的你你我我并不是指kobject_add_internal,而是指kobject与kset的关系,kobject的父节点通常通过kobject->parent指向一个kobject或kset->kobject表现出来,而对kset的操作也主要集中在kset->kobject上,和kobject一样,它也可以是底层空间结构中间层的一员,它的父节点就是kset->kojbect->parent所指向的kobject或kset->kobject,对kobject的大部分操作都适用于对kset->kobject,但是kset除此之外,还集合了所有指向它的kobject的共性操作uevent_ops,现在,换一换眼神,重新抬一头看流程图,会看到kset_create_and_add和kobject_init_add_add中的异同,在kset_create_and_add中除了包含了kobject_init_add_add最最重要的函数kobject_add_internal,还调用了另一个函数kobject_revent(&k->kobj, KOBJ_ADD),槽糕!跑远了,它们的不同也看到了,想说的也说了,该放的也放了,那么kobject_revent这个函数就不说了,不过,要哪天脑子坏掉了,想不起kobject_revent是怎么回事那就悲催了,也让读者跟着作者一块儿脑残,为了防止这类不幸的发生,贯彻作者谦虚而严谨的求学态度(怎么听着跟“为了保护世界和平贯彻爱与真实的邪恶”有点像),决定还是留着一会儿重点分析。
kobject_add_internal的主要作用有两个,一是调用kobject_init将ktype与kobj进行绑定,二是调用sysfs_create_dir在父节点目录下创建一个名为kobj->name的目录,然后调用sys_create_file在该目录下创建相应的属性文件。
接下来,重点分析kobject_uevent,请看源码:
int kobject_uevent(struct kobject *kobj, enum kobject_actionaction)
{
return kobject_uevent_env(kobj,action, NULL);
}
也就是kobject_uevent_env才是kobject_uevent事实上的调用kobject_uevent_env如下:,
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
char *envp_ext[])
{
struct kobj_uevent_env *env;
const char *action_string = kobject_actions[action];
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;
pr_debug("kobject: '%s' (%p): %s\n",
kobject_name(kobj), kobj, __FUNCTION__);
/* search the kset we belong to */
top_kobj = kobj;
while (!top_kobj->kset && top_kobj->parent)//找到最顶层的parent。kobject的parent优先于kobject->kset->kobject
top_kobj = top_kobj->parent;
if (!top_kobj->kset) {
pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "
"without kset!\n", kobject_name(kobj), kobj,
__FUNCTION__);
return -EINVAL;
}
因为对事件的处理函数包含在kobject->kset-> uevent_ops中.要处理事件,就必须要找到上层的一个不为空的kset.上面的代码就是顺着kobject->parent找不到一个不为空的kset.如果不存在这样的kset.就退出
kset = top_kobj->kset;
uevent_ops = kset->uevent_ops;
/* skip the event, if the filter returns zero. */
if(uevent_ops && uevent_ops->filter)//调用kset-> uevent_ops->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, __FUNCTION__);
return 0;
}
/* originating subsystem */
if (uevent_ops && uevent_ops->name)
subsystem = uevent_ops->name(kset, kobj);//获取子系统的名称
else
subsystem = kobject_name(&kset->kobj);
if (!subsystem) {
pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "
"event to drop!\n", kobject_name(kobj), kobj,
__FUNCTION__);
return 0;
}
//找到了不为空的kset.就跟kset-> uevent_ops->filter()匹配.看这个事件是否被过滤.如果没有被过滤掉.就会调用kset-> uevent_ops->name()得到子系统的名称,如果不存在kset-> uevent_ops->name().就会以kobject->name做为子系统名称.
/* environment buffer */
env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
if (!env)
return -ENOMEM;
/* complete object path */
devpath =kobject_get_path(kobj, GFP_KERNEL);
if (!devpath) {
retval = -ENOENT;
goto exit;
}
/* default keys */
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;
/* keys passed in from the caller */
if (envp_ext) {
for (i = 0; envp_ext[i]; i++) {
retval = add_uevent_var(env, envp_ext[i]);
if (retval)
goto exit;
}
}
接下来,就应该设置为调用hotplug设置环境变量了.首先,分配一个struct kobj_uevent_env结构用来存放环境变量的值.然后调用kobject_get_path()用来获得引起事件的kobject在sysfs中的路径.再调用add_uevent_var()将动作代表的字串,kobject路径,子系统名称填充到struct kobj_uevent_env中,如果有指定环境变量,也将其添加进去. kobject_get_path()和add_uevent_var()都比较简单.这里不再详细分析了.请自行查看源代码
/* let the kset specific function add its stuff */
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,
__FUNCTION__, retval);
goto exit;
}
}
/*
* Mark "add" and "remove" events in the object to ensure proper
* events to userspace during automatic cleanup. If the object did
* send an "add" event, "remove" will automatically generated by
* the core, if not already done by the caller.
*/
if (action == KOBJ_ADD)
kobj->state_add_uevent_sent = 1;
else if (action == KOBJ_REMOVE)
kobj->state_remove_uevent_sent = 1;
/* we will send an event, so request a new sequence number */
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;
在这里还会调用kobject->kset-> uevent_ops->uevent().让产生事件的kobject添加环境变量.最后将事件序列添加到环境变量中去.
#if defined(CONFIG_NET)
/* send netlink message */
if (uevent_sock) {
struct sk_buff *skb;
size_t len;
/* allocate message with the maximum possible size */
len = strlen(action_string) + strlen(devpath) + 2;
skb = alloc_skb(len + env->buflen, GFP_KERNEL);
if (skb) {
char *scratch;
/* add header */
scratch = skb_put(skb, len);
sprintf(scratch, "%s@%s", action_string, devpath);
/* copy keys to our continuous event payload buffer */
for (i = 0; i < env->envp_idx; i++) {
len = strlen(env->envp[i]) + 1;
scratch = skb_put(skb, len);
strcpy(scratch, env->envp[i]);
}
NETLINK_CB(skb).dst_group = 1;
netlink_broadcast(uevent_sock, skb, 0, 1, GFP_KERNEL);
}
}
#endif
/* call uevent_helper, usually only enabled during early boot */
if (uevent_helper[0]) {
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;
call_usermodehelper(argv[0], argv, env->envp, UMH_WAIT_EXEC);
}
exit:
kfree(devpath);
kfree(env);
return retval;
}
这么长的一个函数谁都不会想看的,这里以kset_create_and_add中相比于kobject_init_and_add最重要的一个差别为例,也即kobject_uevent(&k->kobj, KOBJ_ADD),进行分析。
首先,认祖宗,从祖宗十八代里中找到最原始kset,但是也有没祖宗的,像那石头缝里蹦出来的猴子,这就是个十足的百分百的黑户,到政府部门办事,那给的绝对是一张黑脸,不给办,直接劈头盖脸一顿臭骂叫你回去。
while (!top_kobj->kset && top_kobj->parent)//找到最顶层的parent。kobject的parent优先于kobject->kset->kobject
top_kobj = top_kobj->parent;
接着,看身份证,查看一下身份证是不是伪造的,当然伪造四六级成绩单,没人管你,鬼才管你,但若然伪造身份证,那也没好下场,直接滚蛋。
if(uevent_ops && uevent_ops->filter)//调用kset-> uevent_ops->filter()匹配.看这个事件是否被过滤
然后,看一下名字,没名没姓,你丫的是火星人啊,从哪来还回哪去。
if (uevent_ops && uevent_ops->name)
subsystem = uevent_ops->name(kset, kobj);//获取子系统的名称
else
subsystem = kobject_name(&kset->kobj);
最后,就是一些身份、背景什么的。
retval = add_uevent_var(env, "ACTION=%s", action_string);
retval = add_uevent_var(env, "DEVPATH=%s", devpath);
retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
for (i = 0; envp_ext[i]; i++) {
retval = add_uevent_var(env, envp_ext[i]);
}
这就是kobject_uevent_env,这么长的程序,真正主要的好像也不算多。
显示器、主机箱、键盘、鼠标,有了这些一台电脑的基本配置已经齐全了,但对于我们刚接触电脑那会儿,怎么去组装起来,怎么开机,怎么运行应用程序,却是脑子一片空白。对于现在的我们,彼此间的关系知道了,总体的概念也有了,但怎么用却还不知道,接着讨论这个问题,至于谁来用这个问题已经不在我们的讨论范围了。
曾经的我以为只要是对的就可以坚持,就一定会有好的结局,后来却成了一种顽疾,才相信坚持其实容易,放弃最难,在得与失之间或快乐或伤心。这里的我也只是学习,也只是在肤浅的层次上班门弄斧,立于硬件之上,而处于驱动之下,隔一纸窗花,所以总有一种说不清道不明的感觉。
我想有些话也许沉默最好,曾经的我也以为清者自清,最大的谦虚莫过于低调,但事实是误解源于不善沟通,而猜忌产生于沉默,有些事情最后还是要澄清的。废话说多了,秉承一贯不解释的风格,以一个例子作为结尾来澄清这种三角关系。
#include<linux/module.h>
#include<linux/fs.h>
#include<linux/kobject.h>
//***********************************************************************************
struct kset kset_Master,kset_Slave;
int uevent_ops_Filter(struct kset *kset, struct kobject *kobj) ;
const char* uevent_ops_Name(struct kset *kset, struct kobject *kobj) ;
int uevent_ops_uevent(struct kset *kset, struct kobject *kobj, struct kobj_uevent_env *env) ;
//************************************************************************************
ssize_t ktype_kobj_kset_test_show1(struct kobject *kobj, struct attribute *attr,char *buf) ;
ssize_t ktype_kobj_kset_test_store1(struct kobject *kobj,struct attribute *attr,const char *buf, size_t count) ;
//************************************************************************************
ssize_t ktype_kobj_kset_test_show2(struct kobject *kobj, struct attribute *attr,char *buf) ;
ssize_t ktype_kobj_kset_test_store2(struct kobject *kobj,struct attribute *attr,const char *buf, size_t count) ;
//************************kset_Master->kobj的属性定义及操作方法*********************************
void ktype_Release1(struct kobject *kobj)
{
printk("wlq1988:ktype_kobj_kset test:release.\n") ;
}
struct attribute master_Attr1[2]=
{
{.name="wlq1988.NO1",
.mode=S_IRWXUGO,},
{.name="wlq1988.NO2",
.mode=S_IRWXUGO,}
};
struct attribute *pKtype_Def_Attr1[]=
{
&master_Attr1[0],
&master_Attr1[1],
NULL,
};
struct sysfs_ops ktype_Sysops1=
{
.show=ktype_kobj_kset_test_show1,
.store=ktype_kobj_kset_test_store1,
};
ssize_t ktype_kobj_kset_test_show1(struct kobject *kobj, struct attribute *attr,char *buf)
{
printk("have show.NO1 or NO2.\n") ;
printk("attrname:%s.\n",attr->name) ;
sprintf(buf,"%s\n",attr->name) ;
return strlen(attr->name)+2 ;
}
ssize_t ktype_kobj_kset_test_store1(struct kobject *kobj,struct attribute *attr,const char *buf, size_t count)
{
printk("have store.NO1 or NO2.\n") ;
printk("write:%s\n",buf) ;
return count ;
}
struct kobj_type ktype1=
{
.release=ktype_Release1,
.sysfs_ops=&ktype_Sysops1,
.default_attrs=pKtype_Def_Attr1,
};
//**************** kset_Slave->kobj的属性定义及操作方法**********************************
void ktype_Release2(struct kobject *kobj)
{
printk("wlq1988:ktype_kobj_kset test:release.\n") ;
}
struct attribute master_Attr2[2]=
{
{.name="wlq1988.NO11",
.mode=S_IRWXUGO,},
{.name="wlq1988.NO12",
.mode=S_IRWXUGO,}
};
struct attribute *pKtype_Def_Attr2[]=
{
&master_Attr2[0],
&master_Attr2[1],
NULL,
};
struct sysfs_ops ktype_Sysops2=
{
.show=ktype_kobj_kset_test_show2,
.store=ktype_kobj_kset_test_store2,
};
ssize_t ktype_kobj_kset_test_show2(struct kobject *kobj, struct attribute *attr,char *buf)
{
printk("have show.NO11 or NO12.\n") ;
printk("attrname:%s.\n",attr->name) ;
sprintf(buf,"%s\n",attr->name) ;
return strlen(attr->name)+2 ;
}
ssize_t ktype_kobj_kset_test_store2(struct kobject *kobj,struct attribute *attr,const char *buf, size_t count)
{
printk("have store.NO11 or NO12.\n") ;
printk("write:%s\n",buf) ;
return count ;
}
struct kobj_type ktype2=
{
.release=ktype_Release2,
.sysfs_ops=&ktype_Sysops2,
.default_attrs=pKtype_Def_Attr2,
};
//****************kset_Master的事件处理方法******************************
struct kset_uevent_ops uevent_ops=
{
.filter=uevent_ops_Filter,
.name=uevent_ops_Name,
.uevent=uevent_ops_uevent,
};
int uevent_ops_Filter(struct kset *kset, struct kobject *kobj)
{
printk("%s..vs.\n",(kset->kobj).name) ;
printk("%s..",kobj->name) ;
printk("%s..",kobj->parent->name) ;
printk("UEVENT:filter,kobj %s.\n",kobj->name) ;
return 1 ;
}
const char* uevent_ops_Name(struct kset *kset, struct kobject *kobj)
{
static char buf[20];
printk("UEVENT:name,kobj %s.\n",kobj->name) ;
sprintf(buf,"%s","kobj_test") ;
return buf ;
}
int uevent_ops_uevent(struct kset *kset, struct kobject *kobj, struct kobj_uevent_env *env)
{
int i=0 ;
printk("UEVENT:uevent,kobj %s.\n",kobj->name) ;
while(i<env->envp_idx)
{
printk("%s.\n",env->envp[i]) ;
i++ ;
}
return 0 ;
}
int __init DEMO_init(void)
{
int ret=0 ;
printk("kset test init.\n") ;
printk("kset master init.\n") ;
kobject_set_name(&kset_Master.kobj,"kset_Master") ;
kset_Master.uevent_ops=&uevent_ops ;
kset_Master.kobj.parent=NULL ;
kset_Master.kobj.kset=NULL ;
kset_Master.kobj.ktype=&ktype1 ;
ret=kset_register(&kset_Master) ;
if(ret)
{
printk("kset_register error.\n") ;
}
printk("kset slave init.\n") ;
kobject_set_name(&kset_Slave.kobj,"kset_Slave") ;
kset_Slave.kobj.kset=&kset_Master ;
kset_Slave.kobj.parent=NULL;
kset_Slave.kobj.ktype=&ktype2 ;
ret=kset_register(&kset_Slave) ;
if(ret)
{
printk("kset_register error.\n") ;
}
return 0 ;
}
void __exit DEMO_exit(void)
{
printk("kobject test exit.\n") ;
kset_unregister(&kset_Slave) ;
kset_unregister(&kset_Master) ;
}
module_init(DEMO_init) ;
module_exit(DEMO_exit) ;
MODULE_LICENSE("GPL") ;
编译生成模块以后,
[root@wlqLinuxFedora10 ktype_kobj_kset]#dmesg
[root@wlqLinuxFedora10 ktype_kobj_kset]#dmesg
[root@wlqLinuxFedora10 ktype_kobj_kset]#rmmod ktype_kobj_kset.ko
[root@wlqLinuxFedora10 ktype_kobj_kset]#dmesg
关于ktype、kobject、kset的关系总算告一段落了,粗浅的记录表不尽我对Linux设备模型的精湛布局的崇拜之情。