Linux设备驱动——驱动模型之基本结构

目录

一、概述

二、kobject

2.1 说明

2.2 数据抽象

2.3 接口

2.4 实现

三 、kset

3.1 说明

3.2 数据抽象

3.3 接口

扫描二维码关注公众号,回复: 6095245 查看本文章

3.4 实现

四、uevent

4.1 说明

4.2 数据抽象

4.3 接口

4.4 实现

四、总结


一、概述

将以前的笔记整理一下

二、kobject

2.1 说明

kobject(kernel object)实际上是顶层抽象基类,它的主要功能有:

  • 引用计数(reference count),当其为0时释放
  • 建立层次结构(hierarchy):通过parent指针,指向上层kobject
  • 利用建立的结构,配合sysfs,将kobject信息导出到用户空间

2.2 数据抽象

struct kobject {
	const char		*name;          /* 外部数据:属性 */
	struct list_head	entry;      /* 内部数据:层次结构 */
	struct kobject		*parent;    /* 外部数据:层次结构 */
	struct kset		*kset;          /* 外部数据:层次结构 */
	struct kobj_type	*ktype;     /* 外部数据:方法 */
	struct kernfs_node	*sd;        /* 内部数据:层次结构 */
	struct kref		kref;           /* 内部数据:继承结构 */
	unsigned int state_initialized:1;
	unsigned int state_in_sysfs:1;
	unsigned int state_add_uevent_sent:1;
	unsigned int state_remove_uevent_sent:1;
	unsigned int uevent_suppress:1;
};
  • 属性 name
  • 结构 entry, parent, kset,sd,kref
  • 方法 ktype

结构分成两部分含义,一部分只在数据结构内部使用,外界不感知,如 parent,sd,kref。一部分要与外部其他模块共同组成结构,如entry,parent

看下ktype:

struct kobj_type {
	void (*release)(struct kobject *kobj);
	const struct sysfs_ops *sysfs_ops;
	struct attribute **default_attrs;
	const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
	const void *(*namespace)(struct kobject *kobj);
};

release和kobject释放有关系,由于object层层被继承,当子类的实例销毁时,重载release就好。

sysfs_ops和default_attrs和sysfs文件系统有关,用于显示kobj相关的信息,实现时逐级调用上层的show/store实现。

2.3 接口

  • void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
  • int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...)
  • int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...)
  • struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)

对一个kobject来说,初始化时需要指定属性,方法,和parent,如果继承kobject,这三个元素可以(必须)重载

2.4 实现

kref 用来实现引用计数

sd用来和sysfs交互,这里不具体展开,认为每个sysfs的节点与一个kobject相关连就好了,这样再通过parent就建立起一个关于kobject的树形结构

kobject_init就是完成动态的初始化——指定方法和内部基本结构 

kobject_add就是将kobj加入到sysfs,设置name,这里需要注意:

  • 如果parent为空,就设置为kobject所属的kset对应的kobject,如果没有对应的kset与之关联,就置空,这意味着kobject的节点在sysfs根目录下。
	parent = kobject_get(kobj->parent);

	/* join kset if set, use it as parent if we do not already have one */
	if (kobj->kset) {
		if (!parent)
			parent = kobject_get(&kobj->kset->kobj);
		kobj_kset_join(kobj);
		kobj->parent = parent;
	}

一般的都通过使用kobject_init_and_add完成上述两个函数的功能。

而kobject_create_and_add用于那些位于sysfs根目录下的kobject(后来发现不一定,parent不一定是NULL),这时候kobject由函数自行分配(kobject_create),这个 函数指定了sysfs根下的 通用方法kobj_type

const struct sysfs_ops kobj_sysfs_ops = {
	.show	= kobj_attr_show,
	.store	= kobj_attr_store,
};

static struct kobj_type dynamic_kobj_ktype = {
	.release	= dynamic_kobj_release,
	.sysfs_ops	= &kobj_sysfs_ops,
};

三 、kset

3.1 说明

kset本身继承了kobject,是一个特殊的kobj,主要功能是
1. 提供kobj的集合
2. 提供对容器内所有成员进行管理的机制:uevnet

3.2 数据抽象

struct kset {
	struct list_head list;
	spinlock_t list_lock;
	struct kobject kobj;
	const struct kset_uevent_ops *uevent_ops;
};
  • 继承 kobj
  • 方法 uevent_ops
  • 结构 list,list_lock

kset要管理位于容器内的kobj,使用list

3.3 接口

  • void kset_init(struct kset *k)
  • int kset_register(struct kset *k)
  • struct kset *kset_create_and_add(const char *name, const struct kset_uevent_ops *uevent_ops,  struct kobject *parent_kobj)

3.4 实现

void kset_init(struct kset *k)
{
	kobject_init_internal(&k->kobj);
	INIT_LIST_HEAD(&k->list);
	spin_lock_init(&k->list_lock);
}

这里kset_init似乎实现的不那么考究,在我看来,既然kset继承于kobject,在初始化时就应该使用kobject提供的外部接口,但是一些object所谓的外部变量如name,ktype该如何传递呢?kset在提供接口时使用了kobject中的internal接口——即不会涉及到外部变量的传递

int kset_register(struct kset *k)
{
	int err;

	kset_init(k);
	err = kobject_add_internal(&k->kobj);
	kobject_uevent(&k->kobj, KOBJ_ADD);

	return 0;
}

可以看到kset_register在实现上和kset_init思路一样。在使用kset_register接口时,要自行初始化父类(对kset来说,name,ktype,parent)和自身uevent_ops

  • uevent_ops
  • name, parent(kset), ktype

kset_create_and_add提供了动态分配的API版本,和kobject类似,该接口没有更上层的kset了:

	/*
	 * 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;
	kset->kobj.kset = NULL;
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);
	error = kset_register(kset);

	return kset;
}

四、uevent

4.1 说明

前面说到kobject提供将内核信息导出到用户空间的功能,这是通过uvent机制实现的

4.2 数据抽象

enum kobject_action {
	KOBJ_ADD,
	KOBJ_REMOVE,
	KOBJ_CHANGE,
	KOBJ_MOVE,
	KOBJ_ONLINE,
	KOBJ_OFFLINE,
	KOBJ_MAX
};

4.3 接口

将指定的kobject指定kobject_actio导出到用户态:

  • int kobject_uevent(struct kobject *kobj, enum kobject_action action)
  • int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, char *envp_ext[])

4.4 实现

int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
		       char *envp_ext[])
{
	/* search the kset we belong to */
	top_kobj = kobj;
	while (!top_kobj->kset && top_kobj->parent)
		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,
			 __func__);
		return -EINVAL;
	}

	kset = top_kobj->kset;
	uevent_ops = kset->uevent_ops;

	if (kobj->uevent_suppress) {
		pr_debug("kobject: '%s' (%p): %s: uevent_suppress "
				 "caused the event to drop!\n",
				 kobject_name(kobj), kobj, __func__);
		return 0;
	}
	/* skip the event, if the filter returns zero. */
	if (uevent_ops && 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, __func__);
			return 0;
		}
}
  • 必须找到kobject对应的kset
  • 检查一些条件,kobj->uevent_suppress设置则丢弃
  • uevent_ops->filter,使用kset管理kobject,如果容器不允许其通过,则丢弃
	/* 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, "%s", envp_ext[i]);
			if (retval)
				goto exit;
		}
	}

	/* 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,
				 __func__, retval);
			goto exit;
		}
	}
  • 如果可以发送,则要将发送的消息整合起来,通过kobj_uevent_env{}
  • 将action_string,devpath,subsystem,额外信息envp_ext,uevent_ops->uevent,uevent_seqnum都加入消息中。

分析一下构造消息的过程:

struct kobj_uevent_env {
	char *argv[3];
	char *envp[UEVENT_NUM_ENVP];
	int envp_idx;
	char buf[UEVENT_BUFFER_SIZE];
	int buflen;
};

将上述消息放到buf中,每单一的消息由envp指向,消息的数量envp_idx,这个功能由add_uevent_var实现,很简单,就不再分析了。

	/* send netlink message */
	list_for_each_entry(ue_sk, &uevent_sock_list, list) {
		struct sock *uevent_sock = ue_sk->sk;
		struct sk_buff *skb;
		size_t len;

		if (!netlink_has_listeners(uevent_sock, 1))
			continue;

		/* 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;
			retval = netlink_broadcast_filtered(uevent_sock, skb,
							    0, 1, GFP_KERNEL,
							    kobj_bcast_filter,
							    kobj);
			/* ENOBUFS should be handled in userspace */
			if (retval == -ENOBUFS || retval == -ESRCH)
				retval = 0;
		} else
			retval = -ENOMEM;
	}
  • 通过netlink构造一个skb将整合过的消息发出去。

五、总结

kobject和kset的关系结构上可以用下图表示:

蓝色箭头构建的是sysfs,黑色箭头方便kset管理容器内的kobject,红色箭头便于kobject找到所属kset

猜你喜欢

转载自blog.csdn.net/whenloce/article/details/88375105
今日推荐