Linux设备驱动模型-Kset

来源:互联网 发布:机械建模软件 编辑:程序博客网 时间:2024/04/30 09:57

前言

当多个kobject属于同一类的时候,为了方便管理,就引入了Kset。Kset可以认为是一组kobject的集合,是kobject的容器。
比如/sys/bus下就属于同一类kobject。
shell@test:/sys/bus $ lsclockeventsclocksourcecontainercoresightcpuevent_sourcehidi2ciiomdio_busmmcplatformscsisdioseriospiusbvirtioworkqueue

数据结构

linux内核使用struct kset结构体代表一个kset结构
struct kset {struct list_head list;spinlock_t list_lock;struct kobject kobj;const struct kset_uevent_ops *uevent_ops;};
list:                  用来将kset下的kobject使用链表管理。
list_lock:         对kset上的list访问操作时,使用spinlock进行互斥保护。
kobj:                kset本身也是一个内核对象,所以需要嵌入kobject进行管理。
uevent_ops:   kset的uevent操作函数集合。当其中的kobject对象的状态发生变化需要通知用户空间的时候,就需要调用uevent_ops中的函数。

struct kset_uevent_ops定义如下:
struct kset_uevent_ops {int (* const filter)(struct kset *kset, struct kobject *kobj);const char *(* const name)(struct kset *kset, struct kobject *kobj);int (* const uevent)(struct kset *kset, struct kobject *kobj,      struct kobj_uevent_env *env);};

kset操作

  • kset_init(初始化一个kset对象)
/** * kset_init - initialize a kset for use * @k: kset */void kset_init(struct kset *k){kobject_init_internal(&k->kobj);                       //初始化kobjectINIT_LIST_HEAD(&k->list);                              //初始化链表spin_lock_init(&k->list_lock);                         //初始化自旋锁}
  • kset_register(初始化然后添加kset)
/** * kset_register - initialize and add a kset. * @k: kset. */int kset_register(struct kset *k){int err;if (!k)return -EINVAL;kset_init(k);                                           //初始化操作err = kobject_add_internal(&k->kobj);                   //将kset->kobject对象添加到sys下,此过程和添加kobject操作一样。if (err)return err;kobject_uevent(&k->kobj, KOBJ_ADD);                     //通过uevent机制通知用户空间return 0;}
关于uevent机制,会在后面的uevent文章中详细说明,此处先跳过。
  • kset_create(动态创建一个Kset)
tatic struct kset *kset_create(const char *name,const struct kset_uevent_ops *uevent_ops,struct kobject *parent_kobj){struct kset *kset;int retval;kset = kzalloc(sizeof(*kset), GFP_KERNEL);if (!kset)return NULL;retval = kobject_set_name(&kset->kobj, "%s", name);                                //设置name域if (retval) {kfree(kset);return NULL;}kset->uevent_ops = uevent_ops;                         //设置uevent_opskset->kobj.parent = parent_kobj;                       //设置parent/* * The kobject of this kset will have a type of kset_ktype and belong to * no kset itself.  That way we can properly free it when it is * finished being used. */kset->kobj.ktype = &kset_ktype;                   //设置ktypekset->kobj.kset = NULL;return kset;}
  • kset_create_and_add(kset_register和kset_create的结合体)
struct kset *kset_create_and_add(const char *name, const struct kset_uevent_ops *uevent_ops, struct kobject *parent_kobj){struct kset *kset;int error;kset = kset_create(name, uevent_ops, parent_kobj);if (!kset)return NULL;error = kset_register(kset);if (error) {kfree(kset);return NULL;}return kset;}

示例

本例看下/sys/module下节点的创建过程。
kernel/kernel/params.c------------------------------------static int __init param_sysfs_init(void){module_kset = kset_create_and_add("module", &module_uevent_ops, NULL);if (!module_kset) {printk(KERN_WARNING "%s (%d): error creating kset\n",__FILE__, __LINE__);return -ENOMEM;}module_sysfs_initialized = 1;version_sysfs_builtin();param_sysfs_builtin();return 0;}
可以看到通过kset_create_and_add创建一个名字为"module"的kset。表现在sys下就是在sys下存在一个module的目录。
root@sp9860g_1h10_3g:/sys/module # lsahci_xgeneaudio_smsgbcmdhdbinderblockbluetoothbrcm_lpmcfg80211configfscoresight_etm4xcpuidledm_bufiodm_moddm_veritydwc3dynamic_debug.....
那module下的这些节点是什么时候创建的?   带着此问题继续看代码。
static void __init param_sysfs_builtin(void){const struct kernel_param *kp;unsigned int name_len;char modname[MODULE_NAME_LEN];for (kp = __start___param; kp < __stop___param; kp++) {char *dot;if (kp->perm == 0)continue;dot = strchr(kp->name, '.');if (!dot) {/* This happens for core_param() */strcpy(modname, "kernel");name_len = 0;} else {name_len = dot - kp->name + 1;strlcpy(modname, kp->name, name_len);}kernel_add_sysfs_param(modname, kp, name_len);}}
对kernel中在.param段的模块参数,通过kernel_add_sysfs_param添加到sys中。而__start___param和__stop___param定义在链接脚本中。
/* Built-in module parameters. */\__param : AT(ADDR(__param) - LOAD_OFFSET) {\VMLINUX_SYMBOL(__start___param) = .;\*(__param)\VMLINUX_SYMBOL(__stop___param) = .;\}
而这些模块参数,是通过module_param等相关函数设置的。
#define module_param(name, type, perm)\module_param_named(name, name, type, perm)

接着上面的分析,看kernel_add_sysfs_param此函数,在此函数中通过locate_module_kobject函数建立起与kset(module_kset)之间的联系。
static struct module_kobject * __init locate_module_kobject(const char *name){struct module_kobject *mk;struct kobject *kobj;int err;kobj = kset_find_obj(module_kset, name);                                 //通过name域找下kobj是否存在,存在就返回,不存在创建新的if (kobj) {mk = to_module_kobject(kobj);                  } else {mk = kzalloc(sizeof(struct module_kobject), GFP_KERNEL);BUG_ON(!mk);mk->mod = THIS_MODULE;mk->kobj.kset = module_kset;err = kobject_init_and_add(&mk->kobj, &module_ktype, NULL,             //设置kset, ktype,  创建新的kobj   "%s", name);#ifdef CONFIG_MODULESif (!err)err = sysfs_create_file(&mk->kobj, &module_uevent.attr);        //创建kobj的属性,#endifif (err) {kobject_put(&mk->kobj);pr_crit("Adding module '%s' to sysfs failed (%d), the system may be unstable.\n",name, err);return NULL;}/* So that we hold reference in both cases. */kobject_get(&mk->kobj);}return mk;}

当用户通过insmod插入一个模块的时候,最后调用到init_module系统调用
SYSCALL_DEFINE3(init_module, void __user *, umod,unsigned long, len, const char __user *, uargs){int err;struct load_info info = { };err = may_init_module();if (err)return err;pr_debug("init_module: umod=%p, len=%lu, uargs=%p\n",       umod, len, uargs);err = copy_module_from_user(umod, len, &info);if (err)return err;return load_module(&info, uargs, 0);}
下面是load_module的调用流程,只关心跟本节相关的。
load_module    ->mod_sysfs_setup           ->module_param_sysfs_setup                 ->add_sysfs_param                      new->grp.name = "parameters";                      new->grp.attrs = attrs;                      new->attrs[num].mattr.show = param_attr_show;                  new->attrs[num].mattr.store = param_attr_store;

最终会在/sys/module下的任何一个模块,都会有一个名字为"parameters"的目录。比如/sys/module/printk
root@test:/sys/module/printk # lsparametersuevent
在parameters下的模块参数有:
root@test:/sys/module/printk/parameters # lsalways_kmsg_dumpconsole_suspendcpuidignore_logleveltime
这些属性都是在printk中通过
module_param(ignore_loglevel, bool, S_IRUGO | S_IWUSR);
相同此种方法创建的。







0 0