本文主要是介绍kobject kset和ktype分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
下面,我开始对kobject kset和ktype做分析 先说说关系,ktype与kobject和kset这两者之前的关系较少,让我画一个图,是这样的 ktype依赖于kobject,kset也依赖于kobject,而kobject有时需要kset(所以用了一个白箭头),不一定需要ktype(真可怜,连白箭头都没有) 首先先说一下这个可有可无的ktype 到/sys/bus/platform下面可以看见一个drivers_autoprobe的文件 cat drivers_autoprobe可以查看这个文件的值 echo 0 > drivers_autoprobe则可以改变这个文件的值 drivers_autoprobe这个文件表示的是是否自动进行初始化 在 void bus_attach_device(struct device *dev) { struct bus_type *bus = dev->bus; int ret = 0; if (bus) { if (bus->p->drivers_autoprobe) ret = device_attach(dev); WARN_ON(ret < 0); if (ret >= 0) klist_add_tail(&dev->knode_bus, &bus->p->klist_devices); } } 中可以看见这么一段代码 if (bus->p->drivers_autoprobe) ret = device_attach(dev); bus->p->drivers_autoprobe的值为真则进行匹配 而drivers_autoprobe这个文件则可以动态的修改这个值选择是否进行匹配 使用外部文件修改内核参数,ktype就是提供了这么一种方法 现在让我们看看ktype是怎么通过kobject进行运作的 首先是ktype及通过ktype进行运作的drivers_autoprobe的注册 ktype的挂载十分简单,因为他是和kobject是一体的 只有这么下面一句 priv->subsys.kobj.ktype = &bus_ktype; 这样就将bus_ktype挂载到了platform_bus_type的kobject上 drivers_autoprobe的注册如下 retval = bus_create_file(bus, &bus_attr_drivers_autoprobe); bus_attr_drivers_autoprobe这个结构由一系列的宏进行组装 static BUS_ATTR(drivers_autoprobe, S_IWUSR | S_IRUGO, show_drivers_autoprobe, store_drivers_autoprobe); #define BUS_ATTR(_name, _mode, _show, _store) \ struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store) #define __ATTR(_name,_mode,_show,_store) { \ .attr = {.name = __stringify(_name), .mode = _mode }, \ .show = _show, \ .store = _store, \ } 最后bus_attr_drivers_autoprobe的模型如下 struct bus_attribute bus_attr_drivers_autoprobe { .attr = { .name = “drivers_autoprobe”, .mode = S_IWUSR | S_IRUGO }, .show = show_drivers_autoprobe, .store = store_drivers_autoprobe, } 进入到bus_create_file中 int bus_create_file(struct bus_type *bus, struct bus_attribute *attr) //参数为(bus, &bus_attr_drivers_autoprobe) { int error; if (bus_get(bus)) { error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr); bus_put(bus); } else error = -EINVAL; return error; } int sysfs_create_file(struct kobject * kobj, const struct attribute * attr) //参数为(&bus->p->subsys.kobj, &attr->attr) { BUG_ON(!kobj || !kobj->sd || !attr); return sysfs_add_file(kobj->sd, attr, SYSFS_KOBJ_ATTR); } int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr,int type) //参数为(&bus->p->subsys.kobj ->sd, &attr->attr, SYSFS_KOBJ_ATTR) { return sysfs_add_file_mode(dir_sd, attr, type, attr->mode); } int sysfs_add_file_mode(struct sysfs_dirent *dir_sd, const struct attribute *attr, int type, mode_t amode) //整理一下参数,现在应该为 //(&platform_bus_type->p->subsys.kobj ->sd, &bus_attr_drivers_autoprobe->attr, SYSFS_KOBJ_ATTR, &bus_attr_drivers_autoprobe->attr->mode) { umode_t mode = (amode & S_IALLUGO) | S_IFREG; struct sysfs_addrm_cxt acxt; struct sysfs_dirent *sd; int rc; //在这一步中可以看出新建了一个节点 sd = sysfs_new_dirent(attr->name, mode, type); if (!sd) return -ENOMEM; //这一步挂载了&bus_attr_drivers_autoprobe->attr到节点中,为以后提取attr及上层结构做准备 sd->s_attr.attr = (void *)attr; // dir_sd也就是上层目录,在这里为platform_bus_type->p->subsys.kobj ->sd //也就是/sys/bus/platform这个目录 sysfs_addrm_start(&acxt, dir_sd); rc = sysfs_add_one(&acxt, sd); sysfs_addrm_finish(&acxt); if (rc) sysfs_put(sd); return rc; } struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type) { char *dup_name = NULL; struct sysfs_dirent *sd; if (type & SYSFS_COPY_NAME) { name = dup_name = kstrdup(name, GFP_KERNEL); if (!name) return NULL; } sd = kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL); if (!sd) goto err_out1; if (sysfs_alloc_ino(&sd->s_ino)) goto err_out2; atomic_set(&sd->s_count, 1); atomic_set(&sd->s_active, 0); sd->s_name = name; //节点的名字为&bus_attr_drivers_autoprobe->attr->name 也就是drivers_autoprobe sd->s_mode = mode; sd->s_flags = type; //节点的type为SYSFS_KOBJ_ATTR return sd; err_out2: kmem_cache_free(sysfs_dir_cachep, sd); err_out1: kfree(dup_name); return NULL; } 现在一切准备就绪,来看看怎么读取吧 首先是open,大概流程可以看我的另一篇文章<从文件到设备>,一直看到ext3_lookup 这里和ext3_lookup不同的是,sys的文件系统是sysfs文件系统,所以应该使用的lookup函数为sysfs_lookup(/fs/sysfs/dir.c) static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { struct dentry *ret = NULL; struct sysfs_dirent *parent_sd = dentry->d_parent->d_fsdata; struct sysfs_dirent *sd; struct inode *inode; mutex_lock(&sysfs_mutex); sd = sysfs_find_dirent(parent_sd, dentry->d_name.name); if (!sd) { ret = ERR_PTR(-ENOENT); goto out_unlock; } //节点的初始化在这里 inode = sysfs_get_inode(sd); if (!inode) { ret = ERR_PTR(-ENOMEM); goto out_unlock; } dentry->d_op = &sysfs_dentry_ops; dentry->d_fsdata = sysfs_get(sd); d_instantiate(dentry, inode); d_rehash(dentry); out_unlock: mutex_unlock(&sysfs_mutex); return ret; } struct inode * sysfs_get_inode(struct sysfs_dirent *sd) { struct inode *inode; inode = iget_locked(sysfs_sb, sd->s_ino); if (inode && (inode->i_state & I_NEW)) //为节点赋值 sysfs_init_inode(sd, inode); return inode; } static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode) { struct bin_attribute *bin_attr; inode->i_blocks = 0; inode->i_mapping->a_ops = &sysfs_aops; inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info; inode->i_op = &sysfs_inode_operations; inode->i_ino = sd->s_ino; lockdep_set_class(&inode->i_mutex, &sysfs_inode_imutex_key); if (sd->s_iattr) { set_inode_attr(inode, sd->s_iattr); } else set_default_inode_attr(inode, sd->s_mode); //判断类型 switch (sysfs_type(sd)) { case SYSFS_DIR: inode->i_op = &sysfs_dir_inode_operations; inode->i_fop = &sysfs_dir_operations; inode->i_nlink = sysfs_count_nlink(sd); break; //还记得在注册的时候有一个参数为SYSFS_KOBJ_ATTR赋到了sd->s_flags上面吧 case SYSFS_KOBJ_ATTR: inode->i_size = PAGE_SIZE; inode->i_fop = &sysfs_file_operations; break; case SYSFS_KOBJ_BIN_ATTR: bin_attr = sd->s_bin_attr.bin_attr; inode->i_size = bin_attr->size; inode->i_fop = &bin_fops; break; case SYSFS_KOBJ_LINK: inode->i_op = &sysfs_symlink_inode_operations; break; default: BUG(); } unlock_new_inode(inode); } sysfs_file_operations的结构如下,之后open和read,write都明了了 const struct file_operations sysfs_file_operations = { .read = sysfs_read_file, .write = sysfs_write_file, .llseek = generic_file_llseek, .open = sysfs_open_file, .release = sysfs_release, .poll = sysfs_poll, }; 有关在哪调用open还是请查阅我的另一篇文章<从文件到设备>中 nameidata_to_filp之后的操作 好的~ 现在进入到了sysfs_open_file中 static int sysfs_open_file(struct inode *inode, struct file *file) { struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; //要重的取值,在这里取得了drivers_autoprobe的目录platform的kproject struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; struct sysfs_buffer *buffer; struct sysfs_ops *ops; int error = -EACCES; if (!sysfs_get_active_two(attr_sd)) return -ENODEV; if (kobj->ktype && kobj->ktype->sysfs_ops) //这里可谓是ktype实现中的核心,在这里ops设置成了platform_bus_type中kobject->ktype的sysfs_ops ops = kobj->ktype->sysfs_ops; else { printk(KERN_ERR "missing sysfs attribute operations for ""kobject: %s\n", kobject_name(kobj)); WARN_ON(1); goto err_out; } if (file->f_mode & FMODE_WRITE) { if (!(inode->i_mode & S_IWUGO) || !ops->store) goto err_out; } if (file->f_mode & FMODE_READ) { if (!(inode->i_mode & S_IRUGO) || !ops->show) goto err_out; } error = -ENOMEM; buffer = kzalloc(sizeof(struct sysfs_buffer), GFP_KERNEL); if (!buffer) goto err_out; mutex_init(&buffer->mutex); buffer->needs_read_fill = 1; //然后将设置好的ops挂载到buffer上 buffer->ops = ops; //再将buffer挂载到file->private_data中 file->private_data = buffer; error = sysfs_get_open_dirent(attr_sd, buffer); if (error) goto err_free; sysfs_put_active_two(attr_sd); return 0; err_free: kfree(buffer); err_out: sysfs_put_active_two(attr_sd); return error; } 现在已经为read和write操作准备好了 马上进入到read操作中 整个流程如上图所示,如何进入到sysfs_read_file在上面open的操作中已经说明了 我们就从sysfs_read_file开始分析(该文件在/fs/sysfs/file.c中) sysfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct sysfs_buffer * buffer = file->private_data; ssize_t retval = 0; mutex_lock(&buffer->mutex); if (buffer->needs_read_fill || *ppos == 0) { //主要操作在fill_read_buffer中 retval = fill_read_buffer(file->f_path.dentry,buffer); if (retval) goto out; } pr_debug("%s: count = %zd, ppos = %lld, buf = %s\n",__func__, count, *ppos, buffer->page); retval = simple_read_from_buffer(buf, count, ppos, buffer->page, buffer->count); out: mutex_unlock(&buffer->mutex); return retval; } static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer) { struct sysfs_dirent *attr_sd = dentry->d_fsdata; //取得父目录的kobject,也就是platform的kobject struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; //还记得这个buffer->ops在什么时候进行赋值的么? struct sysfs_ops * ops = buffer->ops; int ret = 0; ssize_t count; if (!buffer->page) buffer->page = (char *) get_zeroed_page(GFP_KERNEL); if (!buffer->page) return -ENOMEM; if (!sysfs_get_active_two(attr_sd)) return -ENODEV; buffer->event = atomic_read(&attr_sd->s_attr.open->event); //调用ops->show 也就是bus_sysfs_ops->show 具体就是bus_attr_show了 //参数为父目录的kobject, bus_attr_drivers_autoprobe->attr,和一段char信息 count = ops->show(kobj, attr_sd->s_attr.attr, buffer->page); sysfs_put_active_two(attr_sd); if (count >= (ssize_t)PAGE_SIZE) { print_symbol("fill_read_buffer: %s returned bad count\n", (unsigned long)ops->show); /* Try to struggle along */ count = PAGE_SIZE - 1; } if (count >= 0) { buffer->needs_read_fill = 0; buffer->count = count; } else { ret = count; } return ret; } 现在进入bus_attr_show中 static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr,char *buf) { //提取attr的上层结构,也就是bus_attr_drivers_autoprobe struct bus_attribute *bus_attr = to_bus_attr(attr); //提取kobj的上上层结构,也就是bus_type_private struct bus_type_private *bus_priv = to_bus(kobj); ssize_t ret = 0; if (bus_attr->show) //终于到了这里,最后的调用,调用bus_attr_drivers_autoprobe.show ,也就是show_drivers_autoprobe //参数为bus_priv->bus,也就是platform_bus_type , 及一段char信息 ret = bus_attr->show(bus_priv->bus, buf); return ret; } static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf) { return sprintf(buf, "%d\n", bus->p->drivers_autoprobe); } 没什么好介绍了就是打印 buf + bus->p->drivers_autoprobe 从结果来看~ buf是空的 到这里,终于把内核的信息给打印出来了,千辛万苦,层层调用,就是为了取得上层kobject结构,逆运算再取得kobject的上层结构 大家是否对kobject有所了解了呢?~ 在对kobject进行介绍之前 还是先把write操作讲完吧 哈哈~ write操作和read操作重要的步骤基本是一致的,只不过在最后的调用中 static ssize_t store_drivers_autoprobe(struct bus_type *bus, const char *buf, size_t count) { if (buf[0] == '0') bus->p->drivers_autoprobe = 0; else bus->p->drivers_autoprobe = 1; return count; } 不进行打印而对内核的参数进行了修改而已 好~ 现在让我们来看看kobject吧 kobject的结构如下 struct kobject { const char *name; //kobject的名字 struct kref kref; //kobject的原子操作 struct list_head entry; struct kobject *parent; //父对象 struct kset *kset; //父容器 struct kobj_type *ktype; //ktype struct sysfs_dirent *sd; //文件节点 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; }; kobject描述的是较具体的对象,一个设备,一个驱动,一个总线,一类设备 在层次图上可以看出,每个存在于层次图中的设备,驱动,总线,类别都有自己的kobject kobject与kobject之间的层次由kobject中的parent指针决定 而kset指针则表明了kobject的容器 像platform_bus 和test_device的kset都是devices_kset 呢parent和kset有什么不同呢 我认为是人工和默认的区别,看下面这张图 ,蓝框为kset,红框为kobject 容器提供了一种默认的层次~ 但也可以人工设置层次 对于kobject现在我只理解了这么多,欢迎大家指出有疑问的地方 最后是kset,kset比较简单,看下面的结构 struct kset { struct list_head list; spinlock_t list_lock; struct kobject kobj; struct kset_uevent_ops *uevent_ops; }; 对于kset的描述,文档里也有介绍 /** * struct kset - a set of kobjects of a specific type, belonging to a specific subsystem. * * A kset defines a group of kobjects. They can be individually * different "types" but overall these kobjects all want to be grouped * together and operated on in the same manner. ksets are used to * define the attribute callbacks and other common events that happen to * a kobject. 翻译过来大概就是 结构kset,一个指定类型的kobject的集合,属于某一个指定的子系统 kset定义了一组kobject,它们可以是不同类型组成但却希望捆在一起有一个统一的操作 kset通常被定义为回调属性和其他通用的事件发生在kobject上 可能翻译的不是很好,望大家见谅 从结构中能看出kset比kobject多了3个属性 list_head //列表 spinlock_t //共享锁 kset_uevent_ops //uevent操作集 list_head 连接了所有kobject中kset属性指向自己的kobject 而kset_uevent_ops则用于通知机制,由于uevent的作用我也没接触过,所以暂不解析uevent的机制了 写到这里,不知道大家对内核驱动架构中的注册和对kobject的了解有无加深呢? 转帖请注明转自BLOG http://blog.chinaunix.net/u1/57901/ |
这篇关于kobject kset和ktype分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!