Linux设备驱动模型(kobject、kset、ktype、udev)

Linux设备驱动模型的核心即是kobject,最初设计kobject模型的目的是为了实现智能电源管理。后来为了管理日益增加的设备,使得设备在底层都具有同一的接口,使其得以成为设备驱动模型的核心。每个kobject对象都与与sysfs文件系统紧密相连,每个注册的kobject 都对应sysfs文件系统中的一个目录。

首先来分析下kobject ,kobject - kernel object 内核对象
kobject 数据结构 的定义在include/linux/kobject.h

struct kobject {
 	const char  *name; //名称作为一个目录显示在sysfs
	 struct list_head entry; 
	 struct kobject *parent; //父对象
	 struct kset  *kset; //所属kset
 	struct kobj_type *ktype; //属性
	 struct sysfs_dirent *sd; //对应sysfs中的目录
	 struct kref  kref; //该对象的引用计数
	 unsigned int state_initialized:1;
	 unsigned int state_in_sysfs:1; //是否已注册到sysfs
	 unsigned int state_add_uevent_sent:1;
 	unsigned int state_remove_uevent_sent:1;
 	unsigned int uevent_suppress:1;
};

一个设备驱动至少会建立一个kobject,偶尔也有因为功能复杂的原因而建立多个kobject。
每个kobject都会有一个kobj_type属性
关于kobj_type数据结构的定义在include/linux/kobject.h中

struct kobj_type {
 	void (*release)(struct kobject *kobj);/*用于释放kobject占用的资源*/ 
 	const struct sysfs_ops *sysfs_ops;/*提供操作属性的方法*/
 	struct attribute **default_attrs;/*用于保存类型属性列表(指针的指针)*/  
 	fconst struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
	const void *(*namespace)(struct kobject *kobj);
};

每个kobject都必须有一个release方法,但是我们在kobject结构体自身内并没有找到,而是放在了kobj_type中。
关于kobj_type中几个中要的成员:

1、sysfs_ops—sysfs_ops中提供了实现属性的方法:

struct sysfs_ops {	
 	ssize_t (*show)(struct kobject *, struct attribute *,char *);
 	ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
 	const void *(*namespace)(struct kobject *, const struct attribute *);
};

在读取属性文件时会执行show函数,在写属性文件时会执行store。

2、default_attrs—default_attrs指向的地方是一个指针数组,attribute就是kobject的属性:name是属性的名字,在相应的sys目录下表示为以name为名称的属性文件,mode为保护位。

struct attribute {
	 const char  *name;	/*属性文件名称*/
 	umode_t   mode;		
	#ifdef CONFIG_DEBUG_LOCK_ALLOC
 	bool   ignore_lockdep:1;
 	struct lock_class_key *key;
	 struct lock_class_key skey;
#endif
};

kobject 中还包含了一个kset结构体指针,关于kset数据结构定义:

struct kset {
	 struct list_head list; /*链表*/
	 spinlock_t list_lock; /*链表操作时使用的锁*/
	 struct kobject kobj;
 	const struct kset_uevent_ops *uevent_ops; //事件处理
};

kset结构中有一个链表,它目录下的一些相同类型的kobj会在创建时加入这个链表。

内核常用的kobject举例:
sturct kobject *dev_kobj; //设备对象
sturct ko、bject *sysfs_dev_char_kobj; //字符设备对象
sturct kobject *sysfs_dev_block_kobj; //块设备对象
内核中使用的kset举例:
struct kset *bus_kset; //总线相关
sturct kset *class_kset; //类型相同

下面了解一些关于sysfs的相关知识:
sysfs是一个最初基于ramfs且位于内存的文件系统。它提供到处内核数据结构及其属性,以及它们之间的关联到用户空间的方法。sysfs是一个虚拟文件系统,他是kobject对象的完整视图。
以ubuntu下的sys目录为例,ls后可以看多的很多目录
block bus class dev devices firmware fs hypervisor kernel module power
其中:
block 系统中所有的块设备,独立于所连接的总线
bus 系统中用于连接设备的总线
class 设备类型(例如scsi_class),系统中设备的类型(声卡、网卡、显卡);同一类设备可能包含不同总线连接的设备,于是由不同 的驱动程序驱动。
devices 系统中所有的设备
power 处理一些硬件设备电源状态的文件
sysfs在linux中的文件夹、文件全部都是“虚拟出来的”,对文件的读写,不是真的读写,只是对相应函数的调用。

下面我们来结合kobject和sysfs,联系以下两者之间的关系:
从sysfs角度分析,kset代表一个文件夹,而kset文件夹下面的kobj就是这个文件夹里的内容,而内容kobj可能是文件也可能是文件夹。sysfs在linux中的文件夹、文件全部都是“虚拟出来的”,对文件的读写也不是真的读写,只是对相应函数的调用。

接下来就可以看一下在内核源码中在sys目录下的子目录是如何创建的了:
init/main.c中 start_kernel —>rset_init() —> kernel_thread —> kernel_init —> do_basic_setup —> driver_init (drivers/base/init.c)
分析driver_init函数

void __init driver_init(void)
{
	/* These are the core pieces */
	devtmpfs_init();
	devices_init();/*sys 下device、dev目录*/
	buses_init();/*sys下bus目录*/
	classes_init();/*sys 下class目录*/
	firmware_init();/*sys 下firmware目录*/
	hypervisor_init();/*sys 下hypervisor目录*/
	/* These are also core pieces, but must come after the
	* core core pieces.
	*/
	platform_bus_init(); 
	cpu_dev_init();
	memory_dev_init();
	container_dev_init();
	of_core_init();
	}

driver_init函数中执行了xxx_init函数后就会在sys目录下生成了相应的目录。我们以devices_init()为例

int __init devices_init(void)
{
	 devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
	 if (!devices_kset)
	  return -ENOMEM;
	 dev_kobj = kobject_create_and_add("dev", NULL);
	 if (!dev_kobj)
	  goto dev_kobj_err;
	 sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
	 if (!sysfs_dev_block_kobj)
	  goto block_kobj_err;
	 sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
	 if (!sysfs_dev_char_kobj)
	  goto char_kobj_err;
	 return 0;	 
	 char_kobj_err:
	 	kobject_put(sysfs_dev_block_kobj);
	 block_kobj_err:
		 kobject_put(dev_kobj);
	 dev_kobj_err:
		 kset_unregister(devices_kset);
	 return -ENOMEM;
	}

1、首先调用了kset_create_and_add(“devices”, &device_uevent_ops, NULL),创建kset并加入sysfs并返回了devices_kset,这个函数的第个三个参数为NULL表示这个device_kset没有父节点,所以直接会在sys目录下生成名为"devices"的目录。
2、又调用了kobject_create_and_add(“dev”, NULL)创建了dev_kobj 并加入sysfs,调用时第二个参数也就是父节点为NULL,所以在在/sys目录下创建了dev目录。
Linux内核在dev目录下维护了一个按照字符设备和块设备的主次号码(major:minor)链接到真实设备(/sys/devices)的符号链接文件,应用程序可以通过对这些文件的读写和控制来访问实际的设备。
3、分别调用了kobject_create_and_add(“block”, dev_kobj);kobject_create_and_add(“char”, dev_kobj)这次调用第二个参数不是NULL而是在上面创建的dev_kobj,所以最终在在/sys/dev目录下创建了block和char目录。

关于kset_create_and_add函数和kobject_create_and_add函数,位于lib/kobject.c

/**
 * kset_create_and_add - create a struct kset dynamically and add it to sysfs
 *
 * @name: the name for the kset
 * @uevent_ops: a struct kset_uevent_ops for the kset
 * @parent_kobj: the parent kobject of this kset, if any.
 *
 * This function creates a kset structure dynamically and registers it
 * with sysfs.  When you are finished with this structure, call
 * kset_unregister() and the structure will be dynamically freed when it
 * is no longer being used.
 *
 * If the kset was not able to be created, NULL will be returned.
 */
 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;
}

kset_create_and_add中最重要的两个函数:kset_create负责创建,kset_register负责加入sysfs中。

/**
 * kset_create - create a struct kset dynamically
 *
 * @name: the name for the kset
 * @uevent_ops: a struct kset_uevent_ops for the kset
 * @parent_kobj: the parent kobject of this kset, if any.
 *
 * This function creates a kset structure dynamically.  This structure can
 * then be registered with the system and show up in sysfs with a call to
 * kset_register().  When you are finished with this structure, if
 * kset_register() has been called, call kset_unregister() and the
 * structure will be dynamically freed when it is no longer being used.
 *
 * If the kset was not able to be created, NULL will be returned.
 */
static 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);	/*为kset分配内存空间*/
	 if (!kset)
	  	return NULL;
	 retval = kobject_set_name(&kset->kobj, name);	/*设置kset名称*/
	 if (retval) {
	 	 kfree(kset);
	 	 return NULL;
	 }
	 kset->uevent_ops = uevent_ops;
	 kset->kobj.parent = parent_kobj;	/*设置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.type = &kset_ktype;
  	kset->kobj.kset = NULL;

	return kset;
}

kset_create函数主要是为kset结构体申请了存储空间,并对其kset成员进行了初始化,至此还未在sys目录下创建东西。这就是我们说的只有注册的kset才会在sys目录下生成相对应的文件。
我们追踪一下kset_register函数,发现kset_register->kobject_add_internal->create_dir->sysfs_create_dir
populate_dir->sysfs_create_files

/**
 * kset_register - initialize and add a kset.
 * @k: kset.
 */
 int kset_register(struct kset *k)
 {
 	int err;
 	if (!k)
 		return -EINVAL;
 	
 	kset_init(k); /*对传入的kset进行初始化*/
 	err = kobject_add_internal(&k->kobj); 	/*加入k->kobj*/
	 if (err)
 		 return err;
 	kobject_uevent(&k->kobj, KOBJ_ADD);
 		return 0;
 }

kset_init -initialize a kset for use

void kset_init(struct kset *k)
{
	 kobject_init_internal(&k->kobj);	/*kobj->kref引用计数置1,初始化一些kobj的state参数*/
	 INIT_LIST_HEAD(&k->list);	/*初始化链表节点*/
 	spin_lock_init(&k->list_lock);	/*初始化自旋锁*/
}

kobject_add_internal函数

static int kobject_add_internal(struct kobject *kobj)
{
	 int error = 0;
	 struct kobject *parent;

	if (!kobj)
		  return -ENOENT;
	/*kobj->name不存在或为空,报错返回*/
	if (!kobj->name || !kobj->name[0]) {
  		WARN(1, "kobject: (%p): attempted to be registered with empty "
   			 "name!\n", kobj);
  	return -EINVAL;
	/*若kobj->parent存在,则增加kobj->parent引用计数*/
	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为NULL,则使用其自己的kobj作为父节点*/
  			parent = kobject_get(&kobj->kset->kobj);
		  kobj_kset_join(kobj);	/*add the kobject to its kset's list*/
		  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);
		if (error) {	/*创建失败后的处理*/
			kobj_kset_leave(kobj);
 			kobject_put(parent);
 			kobj->parent = NULL;
			
			/* be noisy on error issues */
 			 if (error == -EEXIST)
  				 WARN(1, "%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
   				WARN(1, "%s failed for %s (error: %d parent: %s)\n",
        				__func__, kobject_name(kobj), error,
        				parent ? kobject_name(parent) : "'none'");
        		} else
  			kobj->state_in_sysfs = 1;	/*置1表示已经加入sysfs*/
  		return error;
   }

create_dir函数的内容很少只调用了 sysfs_create_dir 、 populate_dir去生成文件,如果生成失败则调用sysfs_remove_dir移除目录。
create_dir位于lib/kobject.c

static int create_dir(struct kobject *kobj)
{
 	int error = 0;
	 error = sysfs_create_dir(kobj);
 	if (!error) {
  		error = populate_dir(kobj);
 	 if (error)
   		sysfs_remove_dir(kobj);
	 }
	 return error;
}

sysfs_create_dir函数位于fs/sysfs/dir.c

/**
 * sysfs_create_dir - create a directory for an object.
 * @kobj:  object we're creating directory for. 
 */
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);
	
	if (kobj->parent)
	 	parent_sd = kobj->parent->sd;
	 else
  		parent_sd = &sysfs_root;
  	if (!parent_sd)
  		return -ENOENT;
	if (sysfs_ns_type(parent_sd))
 		ns = kobj->ktype->namespace(kobj);
 	type = sysfs_read_ns_type(kobj);

	error = create_dir(kobj, parent_sd, type, ns, kobject_name(kobj), &sd);
	 if (!error)
  		kobj->sd = sd;
	 return error;	

这个函数中会获取传入的kobj的parent信息,如果为NULL会使用&sysfs_root,不为NULL则使用parent->sd的信息。此处就对应了为什么有的目录生成在了sys根目录下,有的sys目录里的子目录下。

create_dir函数里还有一个populate_dir函数
populate_dir位于lib/kobject.c

/*
 * populate_dir - populate directory with attributes.
 * @kobj: object we're working on.
 *
 * Most subsystems have a set of default attributes that are associated
 * with an object that registers with them.  This is a helper called during
 * object registration that loops through the default attributes of the
 * subsystem and creates attributes files for them in sysfs.
 */
 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++) {
 			  error = sysfs_create_file(kobj, attr);
 			  if (error)
   				 break;
 		 }
 	}
 	return error;
}

这个函数的逻辑就很清晰了,获取kobject的属性,然后调用sysfs_create_file生成对应的属性文件加入到kobject对应的sys目录下。

总结一下
1、kset目录下可以放一个或多个kset
2、kset目录下可以放一个或多个kobj
3、kobj不一定需要一个kset
4、kobj目录下还可以放kobj
5、kobj目录下会存在属性文件

猜你喜欢

转载自blog.csdn.net/m0_37105371/article/details/88853540