linux设备驱动模型一三基础结构之Kobject

在linux的驱动表示中,主要有三个基本的结构,分别是kobject,kset,ktype.Kobject,kset,kypte这三个结构是设备模型中的下层架构。模型中的每一个元素都对应一个kobject.kset和ktype可以看成是kobject在层次结构与属性结构方面的扩充。将三者之间的关系用图的方示描述如下:

 

如上图所示:我们知道。在sysfs中每一个目录都对应一个kobject.这些kobject都有自己的parent。在没有指定parent的情况下,都会指向它所属的kset->object。其次,kset也内嵌了kobject.这个kobject又可以指它上一级的parent。就这样。构成了一个空间上面的层次关系。

其实,每个对象都有属性。例如,电源管理,执插拨事性管理等等。因为大部份的同类设备都有相同的属性,因此将这个属性隔离开来,存放在ktype中。这样就可以灵活的管理了.记得在分析sysfs的时候。对于sysfs中的普通文件读写操作都是由kobject->ktype->sysfs_ops来完成的.

kobject

struct kobject {
	const char		*name;				//对应sysfs的目录名
	struct list_head	entry;			//用于将kobj挂在kset->list中
	struct kobject		*parent;		//指向parent kobject,以此形成层次结构(在sysfs就表现为目录结构)
	struct kset		*kset;				//表征该kobj所属的kset
	struct kobj_type	*ktype;			//属性文件,每个kobj或其嵌入的结构对象应该都对应一个kobj_type
	struct sysfs_dirent	*sd;			//对应sysfs对象,在3.14以后的内核中,sysfs基于kernfs来实现。
	struct kref		kref;				//为一个可用于原子操作的引用计数
	unsigned int state_initialized:1;	//记录初始化与否。调用kobject_init()后,会置位
	unsigned int state_in_sysfs:1;		//记录kobj是否注册到sysfs,在kobject_add_internal()中置位
	unsigned int state_add_uevent_sent:1;		//当发送KOBJ_ADD消息时,置位。提示已经向用户空间发送ADD消息
	unsigned int state_remove_uevent_sent:1;	//当发送KOBJ_REMOVE消息时,置位。提示已经向用户空间发送REMOVE消息
	unsigned int uevent_suppress:1;		//如果该字段为1,则表示忽略所有上报的uevent事件
};

在Linux内核里,kobject是组成Linux设备模型的基础,一个kobject对应sysfs里的一个目录。从面向对象的角度来说,kobject可以看作是所有设备对象的基类,因为C语言并没有面向对象的语法,所以一般是把kobject内嵌到其他结构体里来实现类似的作用,这里的其他结构体可以看作是kobject的派生类。Kobject为Linux设备模型提供了很多有用的功能,比如引用计数,接口抽象,父子关系等等,所以,在内核中,没有用kobject直接定义的变量,kobject只是作为一个抽象的基类而存在。一般都是将kobject嵌入到另一个结构,这个结构就可以看做是kobject的一个子类。而kobject的子类会比较关心kobject的属性和方法。

在Linux内核里,kobject是组成Linux设备模型的基础,一个kobject对应sysfs里的一个目录。从面向对象的角度来说,kobject可以看作是所有设备对象的基类,因为C语言并没有面向对象的语法,所以一般是把kobject内嵌到其他结构体里来实现类似的作用,这里的其他结构体可以看作是kobject的派生类。Kobject为Linux设备模型提供了很多有用的功能,比如引用计数,接口抽象,父子关系等等,所以,在内核中,没有用kobject直接定义的变量,kobject只是作为一个抽象的基类而存在。一般都是将kobject嵌入到另一个结构,这个结构就可以看做是kobject的一个子类。而kobject的子类会比较关心kobject的属性和方法。

struct kref//引用计数
{
    atomic_t refcount;
}

kobject的作用:
1、sysfs 表述:在 sysfs 中出现的每个对象都对应一个 kobject, 它和内核交互来创建它的可见表述。
2、数据结构关联:整体来看, 设备模型是一个极端复杂的数据结构,通过其间的大量链接而构成一个多层次的体系结构。kobject 实现了该结构并将其聚合在一起。
3、热插拔事件处理 :kobject 子系统将产生的热插拔事件通知用户空间。

一个kobject对自身并不感兴趣,它存在的意义在于把高级对象连接到设备模型上。因此内核代码很少(甚至不知道)创建一个单独的 kobject;而kobject 被用来控制对大型域(domain)相关对象的访问,所以kobject 被嵌入到其他结构中。kobject 可被看作一个最顶层的基类,其他类都它的派生产物。 kobject 实现了一系列方法,对自身并没有特殊作用,而对其他对象却非常有效。

对于给定的kobject指针,可使用container_of宏得到包含它的结构体的指针

kobject相关操作函数:

1、初始化---kobject_init

void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
	char *err_str;
 
	if (!kobj) {
		err_str = "invalid kobject pointer!";
		goto error;
	}
	if (!ktype) {
		err_str = "must have a ktype to be initialized properly!\n";
		goto error;
	}
	if (kobj->state_initialized) {
		/* do not error out as sometimes we can recover */
		printk(KERN_ERR "kobject (%p): tried to init an initialized "
		       "object, something is seriously wrong.\n", kobj);
		dump_stack();
	}
 
	kobject_init_internal(kobj);//进行主要的成员变量初始化
	kobj->ktype = ktype;  //关联这个kobject类型
	return;
 
error:
	printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);
	dump_stack();
}
static void kobject_init_internal(struct kobject *kobj)
{
	if (!kobj)
		return;
	kref_init(&kobj->kref);//初始化引用计数为1
	INIT_LIST_HEAD(&kobj->entry);//prev和next都指向自己 
	kobj->state_in_sysfs = 0;
	kobj->state_add_uevent_sent = 0;
	kobj->state_remove_uevent_sent = 0;
	kobj->state_initialized = 1;
}

 2、将kobject加入到分层结构-----kobject_add

int kobject_add(struct kobject *kobj, struct kobject *parent,
		const char *fmt, ...)
{
	va_list args;
	int retval;
 
	if (!kobj)
		return -EINVAL;
 
	if (!kobj->state_initialized) {
		printk(KERN_ERR "kobject '%s' (%p): tried to add an "
		       "uninitialized object, something is seriously wrong.\n",
		       kobject_name(kobj), kobj);
		dump_stack();
		return -EINVAL;
	}
	va_start(args, fmt);
	retval = kobject_add_varg(kobj, parent, fmt, args);//主要的add操作
	va_end(args);
 
	return retval;
}
static int kobject_add_varg(struct kobject *kobj, struct kobject *parent,
			    const char *fmt, va_list vargs)
{
	int retval;
 
	retval = kobject_set_name_vargs(kobj, fmt, vargs);//设置kobject的名字。即kobject的name成员
	if (retval) {
		printk(KERN_ERR "kobject: can not set name properly!\n");
		return retval;
	}
	kobj->parent = parent;  //设置kobject的parent
	return kobject_add_internal(kobj);//在sysfs中添加kobjcet信息
}
static int kobject_add_internal(struct kobject *kobj)
{
	int error = 0;
	struct kobject *parent;
 
	if (!kobj)
		return -ENOENT;
 
	if (!kobj->name || !kobj->name[0]) { //如果kobject的名字为空.退出
		WARN(1, "kobject: (%p): attempted to be registered with empty "
			 "name!\n", kobj);
		return -EINVAL;
	}
 
	parent = kobject_get(kobj->parent);//如果parent为真,则增加kobj->kref计数,也就是父节点的引用计数  
 
	/* 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-parent父节点为NULL那么就用kobj->kset->kobj作其父节点,并增加其引用计数  
		kobj_kset_join(kobj);//把kobj的entry成员添加到kobj->kset>list的尾部,现在的层次就是/kobj->kset->list指向kobj->parent ->parent 指向kset->kobj 
		kobj->parent = parent;
	}
 
	pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
		 kobject_name(kobj), kobj, __func__,
		 parent ? kobject_name(parent) : "<NULL>",
		 kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");
 
	error = create_dir(kobj); //利用kobj创建目录和属性文件,其中会判断,如果parent为NULL那么就在sysfs_root下创建 
	if (error) {  //如果创建失败。减少相关的引用计数
		kobj_kset_leave(kobj);
		kobject_put(parent);
		kobj->parent = NULL;
 
		/* be noisy on error issues */
		if (error == -EEXIST)
			printk(KERN_ERR "%s failed for %s with "
			       "-EEXIST, don't try to register things with "
			       "the same name in the same directory.\n",
			       __func__, kobject_name(kobj));
		else
			printk(KERN_ERR "%s failed for %s (%d)\n",
			       __func__, kobject_name(kobj), error);
		dump_stack();
	} else
		kobj->state_in_sysfs = 1; //如果创建成功。将state_in_sysfs建为1。表示该object已经在sysfs中了
 
	return error;
}

这段代码比较简单,它主要完成kobject父结点的判断和选定,然后再调用create_dir

static int create_dir(struct kobject *kobj)
{
	int error = 0;
	if (kobject_name(kobj)) {
		error = sysfs_create_dir(kobj);//为kobject创建目录
		if (!error) {
			error = populate_dir(kobj);  //为kobject->ktype中的属性创建文件
			if (error)
				sysfs_remove_dir(kobj);
		}
	}
	return error;
}

 我们先看一下kobject所表示的目录创建过程。这是在sysfs_create_dir()中完成的

int sysfs_create_dir(struct kobject * kobj)
{
	enum kobj_ns_type type;
	struct sysfs_dirent *parent_sd, *sd;
	const void *ns = NULL;
	int error = 0;
 
	BUG_ON(!kobj);
 
	/*
	如果kobject的parnet存在。就在目录点的目录下创建这个目录。
	如果没有父结点不存在,就在/sys下面创建结点
	*/
	if (kobj->parent)
		parent_sd = kobj->parent->sd;
	else
		parent_sd = &sysfs_root;
 
	if (sysfs_ns_type(parent_sd))
		ns = kobj->ktype->namespace(kobj);
	type = sysfs_read_ns_type(kobj);
 
	 //在sysfs中创建目录
	error = create_dir(kobj, parent_sd, type, ns, kobject_name(kobj), &sd);
	if (!error)
		kobj->sd = sd;
	return error;
}

 接着看为kobject->ktype中的属性创建文件。这是在populate_dir()中完成的

static int populate_dir(struct kobject *kobj)
{
	struct kobj_type *t = get_ktype(kobj);
	struct attribute *attr;
	int error = 0;
	int i;
 
	if (t && t->default_attrs) {
		for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) {//遍历ktype中的属性,为其建立文
			error = sysfs_create_file(kobj, attr); //注意:文件的操作最后都会回溯到ktype->sysfs_ops的show和store这两个函数中. 
			if (error)
				break;
		}
	}
	return error;
}

 另外一个常用的函数就是kobject_init_and_add,直接初始化并进行添加操作

int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
			 struct kobject *parent, const char *fmt, ...)
{
	va_list args;
	int retval;
 
	kobject_init(kobj, ktype);
 
	va_start(args, fmt);
	retval = kobject_add_varg(kobj, parent, fmt, args);
	va_end(args);
 
	return retval;
}

 这里面调用的也就是init和add操作了。

还有一个kobj的删除函数:

void kobject_del(struct kobject *kobj)
{
	if (!kobj)
		return;
 
	sysfs_remove_dir(kobj);//删除sys目录相关文件
	kobj->state_in_sysfs = 0;
	kobj_kset_leave(kobj);//kset链表中删除kobj成员
	kobject_put(kobj->parent);//减少parent计数
	kobj->parent = NULL;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值