前言
当多个kobject属于同一类的时候,为了方便管理,就引入了Kset。Kset可以认为是一组kobject的集合,是kobject的容器。
比如/sys/bus下就属于同一类kobject。
代码语言:javascript复制shell@test:/sys/bus $ ls
clockevents
clocksource
container
coresight
cpu
event_source
hid
i2c
iio
mdio_bus
mmc
platform
scsi
sdio
serio
spi
usb
virtio
workqueue
数据结构
linux内核使用struct kset结构体代表一个kset结构
代码语言:javascript复制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定义如下:
代码语言:javascript复制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); //初始化kobject
INIT_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_ops
kset->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; //设置ktype
kset->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下节点的创建过程。
代码语言:javascript复制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 ksetn",
__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的目录。
代码语言:javascript复制root@sp9860g_1h10_3g:/sys/module # ls
ahci_xgene
audio_smsg
bcmdhd
binder
block
bluetooth
brcm_lpm
cfg80211
configfs
coresight_etm4x
cpuidle
dm_bufio
dm_mod
dm_verity
dwc3
dynamic_debug
.....
那module下的这些节点是什么时候创建的? 带着此问题继续看代码。
代码语言:javascript复制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定义在链接脚本中。
代码语言:javascript复制 /* Built-in module parameters. */
__param : AT(ADDR(__param) - LOAD_OFFSET) {
VMLINUX_SYMBOL(__start___param) = .;
*(__param)
VMLINUX_SYMBOL(__stop___param) = .;
}
而这些模块参数,是通过module_param等相关函数设置的。
代码语言:javascript复制#define module_param(name, type, perm)
module_param_named(name, name, type, perm)
接着上面的分析,看kernel_add_sysfs_param此函数,在此函数中通过locate_module_kobject函数建立起与kset(module_kset)之间的联系。
代码语言:javascript复制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_MODULES
if (!err)
err = sysfs_create_file(&mk->kobj, &module_uevent.attr); //创建kobj的属性,
#endif
if (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系统调用
代码语言:javascript复制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=%pn",
umod, len, uargs);
err = copy_module_from_user(umod, len, &info);
if (err)
return err;
return load_module(&info, uargs, 0);
}
下面是load_module的调用流程,只关心跟本节相关的。
代码语言:javascript复制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
代码语言:javascript复制root@test:/sys/module/printk # ls
parameters
uevent
在parameters下的模块参数有:
代码语言:javascript复制root@test:/sys/module/printk/parameters # ls
always_kmsg_dump
console_suspend
cpuid
ignore_loglevel
time
这些属性都是在printk中通过
代码语言:javascript复制module_param(ignore_loglevel, bool, S_IRUGO | S_IWUSR);
相同此种方法创建的。