七种Linux设备驱动模型之——Kset

前言

当多个kobject属于同一类的时候,为了方便管理,就引入了Kset。Kset可以认为是一组kobject的集合,是kobject的容器。

比如/sys/bus下就属于同一类kobject。

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结构

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);                       //初始化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下节点的创建过程。

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 # 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下的这些节点是什么时候创建的? 带着此问题继续看代码。

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_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系统调用

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 # ls
parameters
uevent

在parameters下的模块参数有:

root@test:/sys/module/printk/parameters # ls
always_kmsg_dump
console_suspend
cpuid
ignore_loglevel
time

这些属性都是在printk中通过

module_param(ignore_loglevel, bool, S_IRUGO | S_IWUSR);

相同此种方法创建的。

原文作者:DragonKingZhu

原文地址:https://cloud.tencent.com/developer/inventory/922/article/1603435(版权归原文作者所有,侵权留言联系删除)

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值