前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux设备驱动模型-Kset

Linux设备驱动模型-Kset

作者头像
DragonKingZhu
发布2020-03-24 11:36:48
3.5K0
发布2020-03-24 11:36:48
举报
文章被收录于专栏:Linux内核深入分析

前言

当多个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对象)
代码语言:javascript
复制
/**
 * 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)
代码语言:javascript
复制
/**
 * 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)
代码语言:javascript
复制
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的结合体)
代码语言:javascript
复制
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 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的目录。

代码语言: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=%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的调用流程,只关心跟本节相关的。

代码语言: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);

相同此种方法创建的。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 数据结构
  • kset操作
  • 示例
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档