第一部分为sysfs操作:该部分涉及目录创建、文件创建、文件打开、文件读取。 1.目录创建 目录创建函数为sysfs_create_dir_ns()。
/** * sysfs_create_dir_ns - create a directory for an object with a namespace tag * @kobj: object we're creating directory for * @ns: the namespace tag to use */ int sysfs_create_dir_ns(struct kobject *kobj, const void *ns) { struct kernfs_node *parent, *kn; BUG_ON(!kobj); // 确定父目录,输入kobj无parent时,指定parent为sysfs的root,表现为/sys/下面的目录 if (kobj->parent) parent = kobj->parent->sd; else parent = sysfs_root_kn; if (!parent) return -ENOENT; kn = kernfs_create_dir_ns(parent, kobject_name(kobj), S_IRWXU | S_IRUGO | S_IXUGO, kobj, ns); kobj->sd = kn; return 0; } /** * kernfs_create_dir_ns - create a directory * @parent: parent in which to create a new directory * @name: name of the new directory * @mode: mode of the new directory * @priv: opaque data associated with the new directory * @ns: optional namespace tag of the directory * * Returns the created node on success, ERR_PTR() value on failure. */ struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, const char *name, umode_t mode, void *priv, const void *ns) { struct kernfs_node *kn; int rc; /* allocate */ kn = kernfs_new_node(parent, name, mode | S_IFDIR, KERNFS_DIR); // 调用kmem_cache_zalloc()完成了目录结点的创建,,flags = KERNFS_DIR; if (!kn) return ERR_PTR(-ENOMEM); kn->dir.root = parent->dir.root; kn->ns = ns; kn->priv = priv; /* link in */ rc = kernfs_add_one(kn); // 将新创建的节点通过kernfs_link_sibling(红黑树插入)加入到sysfs中 if (!rc) return kn; kernfs_put(kn); return ERR_PTR(rc); }2.文件创建 sysfs里的文件创建函数为sysfs_create_file()。
static inline int __must_check sysfs_create_file(struct kobject *kobj, const struct attribute *attr) { return sysfs_create_file_ns(kobj, attr, NULL); } /** * sysfs_create_file_ns - create an attribute file for an object with custom ns * @kobj: object we're creating for * @attr: attribute descriptor * @ns: namespace the new file should belong to */ int sysfs_create_file_ns(struct kobject *kobj, const struct attribute *attr, const void *ns) { BUG_ON(!kobj || !kobj->sd || !attr); return sysfs_add_file_mode_ns(kobj->sd, attr, false, attr->mode, ns); } int sysfs_add_file_mode_ns(struct kernfs_node *parent, const struct attribute *attr, bool is_bin, umode_t mode, const void *ns) { struct lock_class_key *key = NULL; const struct kernfs_ops *ops; struct kernfs_node *kn; loff_t size; if (!is_bin) { struct kobject *kobj = parent->priv; const struct sysfs_ops *sysfs_ops = kobj->ktype->sysfs_ops; // 得到了kobj对应的sysfs_ops, if (sysfs_ops->show && sysfs_ops->store) { if (mode & SYSFS_PREALLOC) ops = &sysfs_prealloc_kfops_rw; else ops = &sysfs_file_kfops_rw; } else if (sysfs_ops->show) { if (mode & SYSFS_PREALLOC) ops = &sysfs_prealloc_kfops_ro; else ops = &sysfs_file_kfops_ro; } else if (sysfs_ops->store) { if (mode & SYSFS_PREALLOC) ops = &sysfs_prealloc_kfops_wo; else ops = &sysfs_file_kfops_wo; } else ops = &sysfs_file_kfops_empty; size = PAGE_SIZE; } else { struct bin_attribute *battr = (void *)attr; if (battr->mmap) ops = &sysfs_bin_kfops_mmap; else if (battr->read && battr->write) ops = &sysfs_bin_kfops_rw; else if (battr->read) ops = &sysfs_bin_kfops_ro; else if (battr->write) ops = &sysfs_bin_kfops_wo; else ops = &sysfs_file_kfops_empty; size = battr->size; } #ifdef CONFIG_DEBUG_LOCK_ALLOC if (!attr->ignore_lockdep) key = attr->key ?: (struct lock_class_key *)&attr->skey; #endif kn = __kernfs_create_file(parent, attr->name, mode & 0777, size, ops, (void *)attr, ns, key); // __kernfs_create_file()与前面目录文件的创建(kernfs_create_dir_ns())非常相似,不同的是目录使用kernfs_node.dir,而文件使用kernfs_node.attr;其实dir和attr属于一个union(参看kernfs_node定义)。 return 0; } /** * __kernfs_create_file - kernfs internal function to create a file * @parent: directory to create the file in * @name: name of the file * @mode: mode of the file * @size: size of the file * @ops: kernfs operations for the file * @priv: private data for the file * @ns: optional namespace tag of the file * @key: lockdep key for the file's active_ref, %NULL to disable lockdep * * Returns the created node on success, ERR_PTR() value on error. */ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent, const char *name, umode_t mode, loff_t size, const struct kernfs_ops *ops, void *priv, const void *ns, struct lock_class_key *key) { struct kernfs_node *kn; unsigned flags; int rc; flags = KERNFS_FILE; kn = kernfs_new_node(parent, name, (mode & S_IALLUGO) | S_IFREG, flags); // 调用kmem_cache_zalloc()完成了文件结点的创建,flags = KERNFS_FILE; if (!kn) return ERR_PTR(-ENOMEM); kn->attr.ops = ops; kn->attr.size = size; kn->ns = ns; kn->priv = priv; #ifdef CONFIG_DEBUG_LOCK_ALLOC if (key) { lockdep_init_map(&kn->dep_map, "s_active", key, 0); kn->flags |= KERNFS_LOCKDEP; } #endif /* * kn->attr.ops is accesible only while holding active ref. We * need to know whether some ops are implemented outside active * ref. Cache their existence in flags. */ if (ops->seq_show) kn->flags |= KERNFS_HAS_SEQ_SHOW; if (ops->mmap) kn->flags |= KERNFS_HAS_MMAP; rc = kernfs_add_one(kn); // 将新创建的节点通过kernfs_link_sibling(红黑树插入)加入到sysfs中 if (rc) { kernfs_put(kn); return ERR_PTR(rc); } return kn; }3.文件打开 要打开文件"/sys/bus/platform/drivers_autoprobe",首先内核会陷入系统调用SYSCALL_DEFINEx(open,),展开即sys_open(),然后开始遍历路径"/sys/bus/platform/drivers_autoprobe",遍历到/sys/时需要切换fs,从rootfs切换到sysfs,然后得到sysfs的root dentry,然后在sysfs文件树里继续前进,最终得到drivers_autoprobe的dentry。有了该dentry,该inode也是已知,然后就得到该文件的fops。以下就从fops开始分析。 sysfs在挂接时,得到了该文件系统的fops就是kernfs_file_fops。因此会调用kernfs_file_fops.open = kernfs_fop_open。 截取该函数的关键代码如下:
static int kernfs_fop_open(struct inode *inode, struct file *file) { struct kernfs_node *kn = file->f_path.dentry->d_fsdata; struct kernfs_root *root = kernfs_root(kn); const struct kernfs_ops *ops; struct kernfs_open_file *of; bool has_read, has_write, has_mmap; int error = -EACCES; ops = kernfs_ops(kn); // 获取ops,此ops是文件创建的时初始化的 has_read = ops->seq_show || ops->read || ops->mmap; has_write = ops->write || ops->mmap; has_mmap = ops->mmap; /* allocate a kernfs_open_file for the file */ error = -ENOMEM; of = kzalloc(sizeof(struct kernfs_open_file), GFP_KERNEL); of->kn = kn; of->file = file; /* * Write path needs to atomic_write_len outside active reference. * Cache it in open_file. See kernfs_fop_write() for details. */ of->atomic_write_len = ops->atomic_write_len; error = -EINVAL; /* * ->seq_show is incompatible with ->prealloc, * as seq_read does its own allocation. * ->read must be used instead. */ if (ops->prealloc && ops->seq_show) goto err_free; if (ops->prealloc) { int len = of->atomic_write_len ?: PAGE_SIZE; of->prealloc_buf = kmalloc(len + 1, GFP_KERNEL); error = -ENOMEM; if (!of->prealloc_buf) goto err_free; mutex_init(&of->prealloc_mutex); } /* * Always instantiate seq_file even if read access doesn't use * seq_file or is not requested. This unifies private data access * and readable regular files are the vast majority anyway. */ if (ops->seq_show) error = seq_open(file, &kernfs_seq_ops); // static const struct seq_operations kernfs_seq_ops = { .start = kernfs_seq_start, .next = kernfs_seq_next, .stop = kernfs_seq_stop, .show = kernfs_seq_show, }; else error = seq_open(file, NULL); ((struct seq_file *)file->private_data)->private = of; /* seq_file clears PWRITE unconditionally, restore it if WRITE */ if (file->f_mode & FMODE_WRITE) file->f_mode |= FMODE_PWRITE; /* make sure we have open node struct */ error = kernfs_get_open_node(kn, of); if (error) goto err_close; /* open succeeded, put active references */ kernfs_put_active(kn); return 0; } // 主要进行了ops的赋值操作。seq_file->op = kernfs_seq_ops;同时把这个seq_file保存在file->private_data里。在文件读操作时,会用到这个ops int seq_open(struct file *file, const struct seq_operations *op) { struct seq_file *p; p = kzalloc(sizeof(*p), GFP_KERNEL); file->private_data = p; p->op = op; p->file = file; file->f_mode &= ~FMODE_PWRITE; return 0; }4.文件读取 sys_read()–> vfs_read(f.file, buf, count, &pos)–> file->f_op->read(file, buf, count, pos) 。 因为sysfs的fops等于kernfs_file_fops。kernfs_file_fops.read=kernfs_fop_read()。
/** * kernfs_fop_read - kernfs vfs read callback * @file: file pointer * @user_buf: data to write * @count: number of bytes * @ppos: starting offset */ static ssize_t kernfs_fop_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct kernfs_open_file *of = kernfs_of(file); if (of->kn->flags & KERNFS_HAS_SEQ_SHOW) return seq_read(file, user_buf, count, ppos); else return kernfs_file_direct_read(of, user_buf, count, ppos); } /** * seq_read -->read() method for sequential files. * @file: the file to read from * @buf: the buffer to read to * @size: the maximum number of bytes to read * @ppos: the current position in the file * * Ready-made ->f_op->read() */ ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) { struct seq_file *m = file->private_data; size_t copied = 0; loff_t pos; size_t n; void *p; int err = 0; m->version = file->f_version; /* Don't assume *ppos is where we left it */ if (unlikely(*ppos != m->read_pos)) { while ((err = traverse(m, *ppos)) == -EAGAIN) // 完成了数据的读取 ; if (err) { /* With prejudice... */ m->read_pos = 0; m->version = 0; m->index = 0; m->count = 0; goto Done; } else { m->read_pos = *ppos; } } /* if not empty - flush it first */ if (m->count) { n = min(m->count, size); err = copy_to_user(buf, m->buf + m->from, n); // 将数据返回到用户空间 } } static int traverse(struct seq_file *m, loff_t offset) { loff_t pos = 0, index; int error = 0; void *p; m->version = 0; index = 0; m->count = m->from = 0; if (!offset) { m->index = index; return 0; } if (!m->buf) { m->buf = seq_buf_alloc(m->size = PAGE_SIZE); if (!m->buf) return -ENOMEM; } p = m->op->start(m, &index); while (p) { error = PTR_ERR(p); if (IS_ERR(p)) break; error = m->op->show(m, p); if (error < 0) break; if (unlikely(error)) { error = 0; m->count = 0; } if (seq_has_overflowed(m)) goto Eoverflow; if (pos + m->count > offset) { m->from = offset - pos; m->count -= m->from; m->index = index; break; } pos += m->count; m->count = 0; if (pos == offset) { index++; m->index = index; break; } p = m->op->next(m, p, &index); } m->op->stop(m, p); m->index = index; return error; Eoverflow: m->op->stop(m, p); kvfree(m->buf); m->count = 0; m->buf = seq_buf_alloc(m->size <<= 1); return !m->buf ? -ENOMEM : -EAGAIN; }主要调用kernfs_seq_ops的start()、show()、next()函数。其中read操作由show()来完成。 以kernfs_seq_show ()为例:
static int kernfs_seq_show(struct seq_file *sf, void *v) { struct kernfs_open_file *of = sf->private; of->event = atomic_read(&of->kn->attr.open->event); return of->kn->attr.ops->seq_show(sf, v); // 建立文件时的struct sysfs_ops *sysfs_ops = kobj->ktype->sysfs_ops, kn->attr.ops = ops; }kobject在注册的时候,也顺带注册了ops,因此该目录下的文件的操作方法就来自于这个kobject的ops。
第二部分为sysfs机制: 1.数据结构:
Kobject是基本数据类型,每个Kobject都会在"/sys/“文件系统中以目录的形式出现。 a.通过parent指针,可以将所有Kobject以层次结构的形式组合起来。 b.使用一个引用计数(reference count),来记录Kobject被引用的次数,并在引用次数变为0时把 它释放,Kobject必须是动态分配的(只有这样才能动态释放) c.和sysfs虚拟文件系统配合,将每一个Kobject及其特性,以文件的形式,开放到用户空间。
struct kobject { const char *name; struct list_head entry; struct kobject *parent; struct kset *kset; struct kobj_type *ktype; struct kernfs_node *sd; /* sysfs directory entry */ struct kref kref; #ifdef CONFIG_DEBUG_KOBJECT_RELEASE struct delayed_work release; #endif 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; };Kset是一个特殊的Kobject(因此它也会在"/sys/“文件系统中以目录的形式出现),它用来集合相似的Kobject(这些Kobject可以是相同属性的,也可以不同属性的)。
struct kset { struct list_head list; // kset目录下的kobj会在创建时链入链表,Kset是一些kobj的集合 spinlock_t list_lock; struct kobject kobj; const struct kset_uevent_ops *uevent_ops; };Ktype代表Kobject(严格地讲,是包含了Kobject的数据结构)的属性操作集合(由于通用性,多个Kobject可能共用同一个属性操作集,因此把Ktype独立出来了)。
struct kobj_type { void (*release)(struct kobject *kobj); // 每个Kobject 必有一个release,在它的成员Ktype内,用以释放包含Kobject的数据结构 const struct sysfs_ops *sysfs_ops; struct attribute **default_attrs; // kobject的属性,name在kobject目录下表现为文件 const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj); const void *(*namespace)(struct kobject *kobj); };Linux内核中,attribute分为普通的attribute和二进制attribute
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 }; struct bin_attribute { // 生成的sysfs文件可以用任何方式读写 struct attribute attr; size_t size; void *private; ssize_t (*read)(struct file *, struct kobject *, struct bin_attribute *, char *, loff_t, size_t); ssize_t (*write)(struct file *, struct kobject *, struct bin_attribute *, char *, loff_t, size_t); int (*mmap)(struct file *, struct kobject *, struct bin_attribute *attr, struct vm_area_struct *vma); };2.数据结构关系:
kset 与 kobj 都是目录,既然是目录,那么应该就是一个树状结构,每一个目录都将有一个父节点: 在kset中使用kset.kobj->parent 指定,在kboject中使用 kobj->parent 指定。
整个树状目录结构,都是通过kobj来构建的,只不过有些kobj嵌在Kset里,分析目录结构时把kset当成一个普通的kobj会好理解很多。
Kobject大多数的使用场景,是内嵌在大型的数据结构中(如Kset、device_driver等),因此这些大型的数据结构,也必须是动态分配、动态释放的。释放的时机是是内嵌的Kobject释放时。
Ktype中的release回调函数负责释放Kobject(甚至是包含Kobject的数据结构),那么Ktype及其内部函数是由上级数据结构所在的模块实现!
在sys下创建目录: 对于kobject:
kobject_create_and_add kobject_init_and_add对于kset:
kset_create_and_add如果 kobject_create_and_add 传入的 parent 参数 是一个普通的kobject ,那么就对应于图中的③与⑤的关系
如果 kobject_create_and_add 传入的 parent 参数 是一个kset->kobject,那么就对应于图中的③与④的关系
如果 kset_create_and_add 传入的 parent 参数 是一个普通的kobject ,那么就对应于图中的④与⑤的关系
如果 kset_create_and_add 传入的 parent 参数 是一个kset->kobject,那么就对应于图中的②与④的关系
如果创建一个 kset 并设置kset.kobject.kset , 然后调用 kset_register,那么就对应于图中④与⑥的关系。
/** * kset_register - initialize and add a kset. * @k: kset. */ int kset_register(struct kset *k) { kset_init(k); err = kobject_add_internal(&k->kobj); kobject_uevent(&k->kobj, KOBJ_ADD); return 0; } static int kobject_add_internal(struct kobject *kobj) { int error = 0; struct kobject *parent; parent = kobject_get(kobj->parent); // kobj是 kset->kobj, 将 kset->kobj 的parent 赋予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); // 如果kset->kobj 的parent不存在,则使用其指向的kset中的kobject作为parent kobj_kset_join(kobj); kobj->parent = parent; } error = create_dir(kobj); kobj->state_in_sysfs = 1; return error; }kobject_init_and_add代码实例:
struct attribute test_attr = { .name = "kobj_config", // 文件名 .mode = S_IRWXUGO, }; static struct attribute *def_attrs[] = { &test_attr, NULL, }; /*当读文件时执行的操作*/ ssize_t kobj_test_show(struct kobject *kobject, struct attribute *attr,char *buf) { printk("have show.\n"); printk("attrname:%s.\n", attr->name); sprintf(buf,"%s\n",attr->name); return strlen(attr->name)+2; } /*当写文件时执行的操作*/ ssize_t kobj_test_store(struct kobject *kobject,struct attribute *attr,const char *buf, size_t count) { printk("havestore\n"); printk("write: %s\n",buf); return count; } //kobject对象的操作 struct sysfs_ops obj_test_sysops = { .show = kobj_test_show, .store = kobj_test_store, }; /*release方法释放该kobject对象*/ void obj_test_release(struct kobject *kobject) { printk("eric_test: release .\n"); } /*定义kobject对象的一些属性及对应的操作*/ struct kobj_type ktype = { .release = obj_test_release, .sysfs_ops=&obj_test_sysops, .default_attrs=def_attrs, }; struct kobject kobj;//声明kobject对象 static int kobj_test_init(void) { printk("kboject test init.\n"); kobject_init_and_add(&kobj,&ktype,NULL,"kobject_test");//初始化kobject对象kobj,并将其注册到linux系统 //kobject_init(&kobj); //kobj.ktype = &ktype; //kobj.parent = NULL; //kobject_set_name(&kobj, "kobject_test"); //err = kobject_add(&kobj); return 0; }4.sysfs_create_group 代码实例:
sysfs_create_group 创建 sysfs接口 在调试驱动,可能需要对驱动里的某些变量进行读写,或函数调用。可通过sysfs接口创建驱动对应的属性,使得可以在用户空间通过sysfs接口的show和store函数与硬件交互。
/* interface for exporting device attributes */ struct device_attribute { struct attribute attr; // attribute嵌入到device_attribute ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf); ssize_t (*store)(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); }; #define DEVICE_ATTR(_name, _mode, _show, _store) \ struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store) #define __ATTR(_name,_mode,_show,_store) { \ .attr = {.name = __stringify(_name), .mode = _mode }, \ .show = _show, \ .store = _store, \ }以下是实例:
static DEVICE_ATTR(gpio, S_IWUSR |S_IRUGO, gpio_show, gpio_store); //属性结构体数组最后一项必须以NULL结尾。 static struct attribute *gpio_attrs[] = { &dev_attr_gpio.attr, NULL } //定义attribute属性结构体数组到属性组中 static const struct attribute_group gpio_attr_grp = { .attrs = gpio_attrs, } . // sysfs_create_group()在kobj目录下创建一个属性集合,并显示集合中的属性文件。如果文件已存在,会报错。 ret = sysfs_create_group(&pdev->dev.kobj,&gpio_attr_grp);5.device_create_file实例
/** * device_create_file - create sysfs attribute file for device. * @dev: device. * @attr: device attribute descriptor. */ int device_create_file(struct device *dev, const struct device_attribute *attr) { int error = 0; if (dev) { WARN(((attr->attr.mode & S_IWUGO) && !attr->store), "Attribute %s: write permission without 'store'\n", attr->attr.name); WARN(((attr->attr.mode & S_IRUGO) && !attr->show), "Attribute %s: read permission without 'show'\n", attr->attr.name); error = sysfs_create_file(&dev->kobj, &attr->attr); } return error; }以下为实例: 在/sys/devices/ 下创建节点:
static DEVICE_ATTR(gpio, S_IWUSR |S_IRUGO, gpio_show, gpio_store); // 使用的device_create_file 创建的节点在/sys/devices/下; 名字应该是dev_attr_gpio结构体中attr的name,为“gpio” device_create_file(dev, &dev_attr_gpio)在sys/class 下创建节点:
gesture_pdata->gesture_class = class_create(THIS_MODULE, "gesture"); gesture_pdata->dev = device_create(gesture_pdata->gesture_class, NULL, 0, NULL, "gesture_ft5x06"); err = device_create_file(gesture_pdata->dev, &dev_attr_enable);本文参考 : https://blog.csdn.net/lizuobin2/article/details/51523693 http://www.wowotech.net/device_model/314.html
