Sysfs 和kobject

目录

1.    sysfs文件系统概览    1

2.    sysfs文件系统挂载    3

2.1 sysfs文件系统类型注册    3

2.2 sysfs挂载    3

3.    sysfs文件系统操作    4

3.1 文件/目录创建    4

3.2 sysfs文件打开    7

3.3 sysfs文件读取    10

sysfs文件系统概览

 

kobject框架

 

sysfs和kobject实现为两套独立的框架。一个kobject在sysfs中表现为一个目录,属性在sysfs中表现为一个文件。kernfs_node->priv指向kobject;kobject->sd指向kernfs_node,这实现了kobject框架和sysfs的连接。总的来说sysfs实现了文件/目录结构层次的管理;kobject框架提供了底层文件/目录的操作方法。

数据结构主要成员说明

struct kernfs_super_info

struct super_block *sb

指向vfs的super_block

struct kernfs_root *root

指向sysfs_root

struct list_head node

链接到sysfs_root->supers

 

struct kernfs_root

struct kernfs_node *kn

指向根目录对应的kernfs_node

struct list_head supers

用与链接挂载到更目录下文件系统的kernfs_super_info

 

sysfs中的每一个目录或者文件都有一个kernfs_node来描述,就像vfs中的inode一样。

struct kernfs_node

struct kernfs_node *parent

指向父目录

const char *name

文件或者目录名

struct rb_node rb

用于连接到父目录的红黑树中

 

 

 

 

union {

struct kernfs_elem_dir dir;

struct kernfs_elem_symlink symlink;

struct kernfs_elem_attr attr;

};

struct kernfs_elem_dir {

unsigned long subdirs;

struct rb_root children; //用于挂接子目录的红黑树

struct kernfs_root *root;

};

struct kernfs_elem_symlink {

struct kernfs_node *target_kn;

};

struct kernfs_elem_attr {

//属性文件在sysfs中的通用操作函数集,比如sysfs_prealloc_kfops_rw

const struct kernfs_ops *ops;

struct kernfs_open_node *open;

……

};

void *priv

指向kobject

unsigned short flags

可以是这三个值:KERNFS_DIR , KERNFS_FILE ,KERNFS_LINK

 

kset为一类kobject的集合,比如devices_kset,在sysfs中对应目录 '/sys/devices'

struct kset

struct list_head list

用于链接kset下面的kobject

struct kobject kobj

Kset自己的kobject,kset在sysfs中也是一个目录

const struct kset_uevent_ops *uevent_ops

例如device_uevent_ops

 

设备驱动中device,bus等结构体中都会内嵌一个kobject,会在sysfs创建一个目录

struct kobject

const char *name

Kobject都是嵌入其他结构中,往往与kobject父结构名相同

struct list_head entry

链接到kset中

struct kobject *parent

指向父kobject

struct kset *kset

指向所属的kset

struct kobj_type *ktype

例如device_ktype,其中包含一组操作函数集

struct kernfs_node *sd

指向sysfs中的kernfs_node

struct kref kref

引用计数


还有一个属性结构体就不具体讲解了,不同属性类型会有不同的结构实现,但是都内嵌一个struct attribute。

sysfs文件系统挂载

2.1 sysfs文件系统类型注册


    
    
  1. static struct file_system_type sysfs_fs_type = {
  2. .name = "sysfs",
  3. .mount = sysfs_mount,
  4. .kill_sb = sysfs_kill_sb,
  5. .fs_flags = FS_USERNS_MOUNT,
  6. };
  7. int __init sysfs_init(void)
  8. {
  9. int err;
  10. sysfs_root = kernfs_create_root( NULL, KERNFS_ROOT_EXTRA_OPEN_PERM_CHECK,
  11. NULL); // 创建代表sysfs根目录的kernfs_node
  12. if ( IS_ERR(sysfs_root))
  13. return PTR_ERR(sysfs_root);
  14. sysfs_root_kn = sysfs_root->kn;
  15. err = register_filesystem(&sysfs_fs_type); // 将sysfs_fs_type 注册到全局链表file_systems中
  16. if (err) {
  17. kernfs_destroy_root(sysfs_root);
  18. return err;
  19. }
  20. return 0;
  21. }

2.2 sysfs挂载

sysfs挂载流程如下:

 

sget_userns :分配super_block并且让sb->s_fs_info结构体 kernfs_super_info

kernfs_fill_super:初始化super_block中的相关字段,创建根目录对应的dentry和inode


   
   
  1. static int kernfs_fill_super(struct super_block *sb, unsigned long magic)
  2. {
  3. struct kernfs_super_info *info = kernfs_info(sb);
  4. struct inode *inode;
  5. struct dentry *root;
  6. info->sb = sb;
  7. sb->s_iflags |= SB_I_NOEXEC | SB_I_NODEV;
  8. sb->s_blocksize = PAGE_SIZE;
  9. sb->s_blocksize_bits = PAGE_SHIFT;
  10. sb->s_magic = magic;
  11. sb->s_op = &kernfs_sops;
  12. sb->s_xattr = kernfs_xattr_handlers;
  13. sb->s_time_gran = 1;
  14. mutex_lock(&kernfs_mutex);
  15. inode = kernfs_get_inode(sb, info->root->kn); //创建根目录对应的inode
  16. mutex_unlock(&kernfs_mutex);
  17. root = d_make_root(inode); //创建根目录对应的dentry
  18. kernfs_get(info->root->kn);
  19. root->d_fsdata = info->root->kn; //连接更目录的dentry和kernfs_node
  20. sb->s_root = root;
  21. sb->s_d_op = &kernfs_dops;
  22. return 0;
  23. }sysfs文件系统操作

3.1 文件/目录创建

下面以一个例子来讲解sysfs文件/目录创建过程。例子来源linux-4.12.3/drivers/input/evdev.c


   
   
  1. static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
  2. const struct input_device_id *id)
  3. {
  4. struct evdev *evdev;
  5. int minor;
  6. int dev_no;
  7. int error;
  8. evdev = kzalloc( sizeof( struct evdev), GFP_KERNEL);
  9. ......
  10. evdev->dev.parent = &dev->dev;
  11. evdev->dev.release = evdev_free;
  12. device_initialize(&evdev->dev); //初始化dev相关字段,尤其是我们关注的kobject
  13. ......
  14. error = cdev_device_add(&evdev->cdev, &evdev->dev); //将dev添加到内核相关数据结构中并且在proc和sysfs中创建相关文件
  15. ......
  16. }
  17. void device_initialize(struct device *dev)
  18. {
  19. dev->kobj.kset = devices_kset;
  20. kobject_init(&dev->kobj, &device_ktype); //让kobj->ktype指向device_ktype,后面我们将会涉及到
  21. ......
  22. }

函数cdev_device_add会进一步调用到device_add:


   
   
  1. int device_add(struct device *dev)
  2. {
  3. struct device *parent;
  4. struct kobject *kobj;
  5. ......
  6. error = kobject_add(&dev->kobj, dev->kobj.parent, NULL); //为dev在sysfs中创建目录
  7. if (error) {
  8. glue_dir = get_glue_dir(dev);
  9. goto Error;
  10. }
  11. error = device_create_file(dev, &dev_attr_uevent); //在前面创建的目录中创建属性文件uevent
  12. if (error)
  13. goto attrError;
  14. ......
  15. }

目录创建

kobject在sysfs中表示为一个目录,kobject_add在sysfs中创建一个目录,其流程如下:



sysfs_create_dir_ns: 在sysfs中创建目录的接口。sysfs与kobject关系紧密但kobject并不是默认集成到sysfs

    中,sysfs中目录也不是一定有一个kobject与之对应。

populate_dir:为默认属性列表kobj->ktype-> default_attrs在目录kobject下创建对应文件


   
   
  1. int sysfs_create_dir_ns(struct kobject *kobj, const void *ns)
  2. {
  3. struct kernfs_node *parent, *kn;
  4. if (kobj->parent)
  5. parent = kobj->parent->sd; //获取父目录对应的kernfs_node
  6. else
  7. parent = sysfs_root_kn; //如果在sysfs根目录下直接创建目录,sysfs_root_kn在2.1节有讲到过
  8. kn = kernfs_create_dir_ns(parent, kobject_name(kobj), //完成在sysfs中创建目录的实际工作
  9. S_IRWXU | S_IRUGO | S_IXUGO, kobj, ns);
  10. …….
  11. kobj->sd = kn; //关联kobject框架和sysfs,可以通过kobject找到在sysfs中对应的kernfs_node
  12. return 0;
  13. }

从函数kernfs_create_dir_ns的参数可以看出sysfs和kobject是相互独立的,sysfs目录和kobject并不是天生一一对应。


   
   
  1. struct kernfs_node * kernfs_create_dir_ns( struct kernfs_node *parent,
  2. const char *name, umode_t mode,
  3. void *priv, const void *ns)
  4. {
  5. struct kernfs_node *kn;
  6. int rc;
  7. //sysfs中目录或者文件都有一个kernfs_node来描述,这里就是创建一个用于描述sysfs中目录的kernfs_node。
  8. // S_IFDIR和KERNFS_DIR表明创建的是一个目录
  9. kn = kernfs_new_node(parent, name, mode | S_IFDIR, KERNFS_DIR);
  10. kn->dir.root = parent->dir.root;
  11. kn->ns = ns;
  12. kn->priv = priv; //这里priv传入的是kobj,可以通过kernfs_node找到对应的kobject
  13. rc = kernfs_add_one(kn); //将kn连接到父目录的红黑树中 kn->parent->dir.children.rb_node
  14. }

文件创建

截取前面提到的文件创建代码:


   
   
  1. int device_add(struct device *dev)
  2. {
  3. ......
  4. error = device_create_file(dev, &dev_attr_uevent);
  5. ......
  6. }

在进一步文件闯将讲解之前先认识下dev_attr_uevent。

static DEVICE_ATTR_RW(uevent);
   
   

中间过程就不讲了,宏展开之后如下:


   
   
  1. struct device_attribute dev_attr_ uevent = {
  2. .attr = {
  3. .name = __stringify(_name),
  4. .mode = VERIFY_OCTAL_PERMISSIONS(_mode)
  5. },
  6. .show = uevent _show,
  7. .store = uevent_store,
  8. }

函数device_create_file最终会调用到sysfs_add_file_mode_ns,参数attr也就是前面传递下来的属性dev_attr_uevent,下面看在kobject对应目录下创建文件的具体过程:


   
   
  1. int sysfs_add_file_mode_ns(struct kernfs_node *parent,
  2. const struct attribute *attr, bool is_bin,
  3. umode_t mode, const void *ns)
  4. {
  5. struct lock_class_key *key = NULL;
  6. const struct kernfs_ops *ops;
  7. struct kernfs_node *kn;
  8. loff_t size;
  9. if (!is_bin) {
  10. struct kobject *kobj = parent->priv;
  11. const struct sysfs_ops *sysfs_ops = kobj->ktype->sysfs_ops; //这里的ktype就是3.1节提到的device_ktype,这个后面会有讲解。
  12. if (sysfs_ops->show && sysfs_ops->store) {
  13. if (mode & SYSFS_PREALLOC)
  14. ops = &sysfs_prealloc_kfops_rw; //根据不同的mode给属性设置不同的ops
  15. else
  16. ops = &sysfs_file_kfops_rw;
  17. } else if (sysfs_ops->show) {
  18. ……
  19. size = PAGE_SIZE;
  20. } else {
  21. ……
  22. }
  23. kn = __kernfs_create_file(parent, attr->name, mode & 0777, size, ops,
  24. ( void *)attr, ns, key); //创建属性文件,创建文件对应的kernfs_node
  25. ……
  26. }
  27. struct kernfs_node *__kernfs_create_file( struct kernfs_node *parent,
  28. const char *name,
  29. umode_t mode, loff_t size,
  30. const struct kernfs_ops *ops,
  31. void *priv, const void *ns,
  32. struct lock_class_key *key)
  33. {
  34. struct kernfs_node *kn;
  35. unsigned flags;
  36. int rc;
  37. flags = KERNFS_FILE;
  38. /*分配kernfs_node初始化其相关字段,并让kn->parent指向父目录的kernfs_node,也就是dev->kobj->sd,标识S_IFREG表明创建的是普通文件*/
  39. kn = kernfs_new_node(parent, name, (mode & S_IALLUGO) | S_IFREG, flags);
  40. kn->attr.ops = ops; //设置属性文件的ops,这个后续讲解文件读取的时候会详细讲解
  41. kn->attr.size = size;
  42. kn->ns = ns;
  43. kn->priv = priv; //让kn->priv 指向dev_attr_uevent->attr
  44. if (ops->seq_show)
  45. kn->flags |= KERNFS_HAS_SEQ_SHOW;
  46. if (ops->mmap)
  47. kn->flags |= KERNFS_HAS_MMAP;
  48. if (ops->release)
  49. kn->flags |= KERNFS_HAS_RELEASE;
  50. rc = kernfs_add_one(kn); //将kn连接到父目录的红黑树中 kn->parent->dir.children.rb_node
  51. return kn;
  52. }

3.2 sysfs文件打开


dir_inode->i_op->lookup :对应函数kernfs_iop_lookup,用于查找目录下面的文件。

f->f_op->open: 这里对应函数kernfs_fop_open

 

文件查找


   
   
  1. const struct inode_operations kernfs_dir_iops = {
  2. .lookup = kernfs_iop_lookup,
  3. ……
  4. .mkdir = kernfs_iop_mkdir,
  5. .rmdir = kernfs_iop_rmdir,
  6. .rename = kernfs_iop_rename,
  7. };
  8. static struct dentry * kernfs_iop_lookup( struct inode *dir,
  9. struct dentry *dentry,
  10. unsigned int flags)
  11. {
  12. struct dentry *ret;
  13. struct kernfs_node *parent = dentry->d_parent->d_fsdata; //获取父目录在sysfs中的kernfs_node
  14. struct kernfs_node *kn;
  15. struct inode *inode;
  16. const void *ns = NULL;
  17. // 在parent->dir.children.rb_node中查找文件对应的kernfs_node
  18. kn = kernfs_find_ns(parent, dentry->d_name.name, ns);
  19. kernfs_get(kn);
  20. dentry->d_fsdata = kn; //连接vfs中dentry和sysfs中的kernfs_node
  21. inode = kernfs_get_inode(dir->i_sb, kn); //创建文件对应的inode,这个后续细讲
  22. ret = d_splice_alias(inode, dentry); //主要工作是dentry与inode以及将dentry加入到缓存中
  23. }

函数kernfs_get_inode中分配inode结构然后调用kernfs_init_inode初始化inode相关成员


   
   
  1. static void kernfs_init_inode(struct kernfs_node *kn, struct inode *inode)
  2. {
  3. kernfs_get(kn);
  4. inode->i_private = kn;
  5. inode->i_mapping->a_ops = &kernfs_aops;
  6. inode->i_op = &kernfs_iops;
  7. switch ( kernfs_type(kn)) {
  8. case KERNFS_DIR: //如果是目录的inode就设置i_op和i_fop分别为kernfs_dir_iops和kernfs_dir_fops
  9. inode->i_op = &kernfs_dir_iops;
  10. inode->i_fop = &kernfs_dir_fops;
  11. if (kn->flags & KERNFS_EMPTY_DIR)
  12. make_empty_dir_inode(inode);
  13. break;
  14. case KERNFS_FILE: //如果inode是普通文件就设置i_fop为kernfs_file_fops
  15. inode->i_size = kn->attr.size;
  16. inode->i_fop = &kernfs_file_fops;
  17. break;
  18. case KERNFS_LINK:
  19. inode->i_op = &kernfs_symlink_iops;
  20. break;
  21. default:
  22. BUG();
  23. }
  24. unlock_new_inode(inode);
  25. }

文件打开


   
   
  1. const struct file_operations kernfs_file_fops = {
  2. .read = kernfs_fop_read,
  3. .write = kernfs_fop_write,
  4. .llseek = generic_file_llseek,
  5. .mmap = kernfs_fop_mmap,
  6. .open = kernfs_fop_open,
  7. .release = kernfs_fop_release,
  8. .poll = kernfs_fop_poll,
  9. .fsync = noop_fsync,
  10. };
  11. static int kernfs_fop_open(struct inode *inode, struct file *file)
  12. {
  13. struct kernfs_node *kn = file->f_path.dentry->d_fsdata;
  14. struct kernfs_root *root = kernfs_root(kn);
  15. const struct kernfs_ops *ops;
  16. struct kernfs_open_file *of;
  17. bool has_read, has_write, has_mmap;
  18. ops = kernfs_ops(kn);
  19. has_read = ops->seq_show || ops->read || ops->mmap;
  20. has_write = ops->write || ops->mmap;
  21. has_mmap = ops->mmap;
  22. //分配一个kernfs_open_file来管理一个打开的sysfs文件
  23. of = kzalloc( sizeof( struct kernfs_open_file), GFP_KERNEL);
  24. if (!of)
  25. goto err_out;
  26. of->kn = kn; //连接kernfs_open_file和kernfs_node
  27. of->file = file;
  28. if (ops->prealloc) {
  29. int len = of->atomic_write_len ?: PAGE_SIZE;
  30. of->prealloc_buf = kmalloc(len + 1, GFP_KERNEL); //分配读取数据所需内存
  31. error = -ENOMEM;
  32. if (!of->prealloc_buf)
  33. goto err_free;
  34. mutex_init(&of->prealloc_mutex);
  35. }
  36. if (ops->seq_show)
  37. error = seq_open(file, &kernfs_seq_ops);
  38. else
  39. error = seq_open(file, NULL); //分配seq_file并让file->private_data指向seq_file
  40. if (error)
  41. goto err_free;
  42. of->seq_file = file->private_data;
  43. of->seq_file-> private = of; //在后续读写操作中可以通过file->private_data-> private得到kernfs_open_file
  44. ……
  45. }

3.3 sysfs文件读取


    
    
  1. const struct file_operations kernfs_file_fops = {
  2. .read = kernfs_fop_read,
  3. .write = kernfs_fop_write,
  4. .llseek = generic_file_llseek,
  5. .mmap = kernfs_fop_mmap,
  6. .open = kernfs_fop_open,
  7. .release = kernfs_fop_release,
  8. .poll = kernfs_fop_poll,
  9. .fsync = noop_fsync,
  10. };



kn->attr.ops 在3.1节"文件创建"中初始化为sysfs_prealloc_kfops_rw

kobj->ktype->sysfs_ops: 在3.1节中将kobj->ktype设置为device_ktype,device_ktype是静态定义的

    device_ktype. sysfs_ops设置为dev_sysfs_ops;

dev_attr: 在3.1节"文件创建"中我们的例子就是创建属性文件dev_attr_uevent. attr,uevent属性是通过

    宏DEVICE_ATTR_RW(uevent)静态定义的;

kset->uevent_ops: 在3.1节的例子中我们将dev->kobj.kset 设置为devices_kset,这里的kset->uevent_ops

    就是device_uevent_ops.


   
   
  1. static ssize_t uevent_show(struct device *dev, struct device_attribute *attr,
  2. char *buf)
  3. {
  4. struct kobject *top_kobj;
  5. struct kset *kset;
  6. struct kobj_uevent_env *env = NULL;
  7. ……
  8. top_kobj = &dev->kobj;
  9. while (!top_kobj->kset && top_kobj->parent)
  10. top_kobj = top_kobj->parent;
  11. if (!top_kobj->kset)
  12. goto out;
  13. kset = top_kobj->kset;
  14. if (!kset->uevent_ops || !kset->uevent_ops->uevent)
  15. goto out;
  16. env = kzalloc( sizeof( struct kobj_uevent_env), GFP_KERNEL);
  17. if (!env)
  18. return -ENOMEM;
  19. retval = kset->uevent_ops-> uevent(kset, &dev->kobj, env);
  20. if (retval)
  21. goto out;
  22. for (i = 0; i < env->envp_idx; i++)
  23. count += sprintf(&buf[count], "%s\n", env->envp[i]);
  24. return count;
  25. }
  26. static int dev_uevent(struct kset *kset, struct kobject *kobj,
  27. struct kobj_uevent_env *env)
  28. {
  29. struct device *dev = kobj_to_dev(kobj);
  30. int retval = 0;
  31. if ( MAJOR(dev->devt)) {
  32. const char *tmp;
  33. const char *name;
  34. umode_t mode = 0;
  35. kuid_t uid = GLOBAL_ROOT_UID;
  36. kgid_t gid = GLOBAL_ROOT_GID;
  37. add_uevent_var(env, "MAJOR=%u", MAJOR(dev->devt));
  38. add_uevent_var(env, "MINOR=%u", MINOR(dev->devt));
  39. name = device_get_devnode(dev, &mode, &uid, &gid, &tmp);
  40. if (name) {
  41. add_uevent_var(env, "DEVNAME=%s", name);
  42. ……
  43. }
  44. }
  45. ......
  46. }


 


转载 https://blog.csdn.net/chenying126/article/details/78079942
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
kobject是Linux内核中的一个重要概念,它是内核对象的抽象表示,可以用于管理内核中的各种对象,如设备、文件系统、网络等。下面是一个kobject使用的示例: 首先,我们需要定义一个kobject结构体,如下所示: ``` struct my_kobject { struct kobject kobj; int value; }; ``` 其中,kobj是kobject结构体的一部分,用于表示kobject对象本身。value是我们自定义的一个属性,用于存储一些数据。 接下来,我们需要初始化kobject对象,可以使用kobject_init函数,如下所示: ``` struct my_kobject *my_obj; my_obj = kzalloc(sizeof(*my_obj), GFP_KERNEL); if (!my_obj) return -ENOMEM; kobject_init(&my_obj->kobj, &my_ktype); my_obj->kobj.parent = &my_parent->kobj; ``` 其中,my_ktype是我们自定义的一个ktype结构体,用于描述kobject对象的属性和操作。my_parent是我们定义的一个父kobject对象,用于管理多个子kobject对象。 最后,我们可以使用sysfs接口来访问kobject对象的属性和操作,如下所示: ``` static ssize_t value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { struct my_kobject *my_obj = container_of(kobj, struct my_kobject, kobj); return sprintf(buf, "%d\n", my_obj->value); } static ssize_t value_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { struct my_kobject *my_obj = container_of(kobj, struct my_kobject, kobj); sscanf(buf, "%d", &my_obj->value); return count; } static struct kobj_attribute value_attribute = __ATTR(value, 0664, value_show, value_store); static struct attribute *my_attrs[] = { &value_attribute.attr, NULL, }; static struct attribute_group my_attr_group = { .attrs = my_attrs, }; static struct ktype my_ktype = { .sysfs_ops = &my_sysfs_ops, .default_attrs = my_attrs, }; static struct sysfs_ops my_sysfs_ops = { .show = my_show, .store = my_store, }; struct my_kobject *my_obj; my_obj = kzalloc(sizeof(*my_obj), GFP_KERNEL); if (!my_obj) return -ENOMEM; kobject_init(&my_obj->kobj, &my_ktype); my_obj->kobj.parent = &my_parent->kobj; if (kobject_add(&my_obj->kobj, &my_parent->kobj, "my_obj") < 0) { kobject_put(&my_obj->kobj); return -1; } if (sysfs_create_group(&my_obj->kobj, &my_attr_group) < 0) { kobject_put(&my_obj->kobj); return -1; } ``` 其中,value_show和value_store是我们自定义的两个属性操作函数,用于读取和写入value属性。my_attrs是我们自定义的一个属性数组,用于描述kobject对象的属性。my_attr_group是我们自定义的一个属性组,用于将属性数组添加到kobject对象中。my_ktype是我们自定义的一个ktype结构体,用于描述kobject对象的sysfs接口。my_sysfs_ops是我们自定义的一个sysfs_ops结构体,用于描述sysfs接口的操作函数。 以上就是一个简单的kobject使用示例,通过sysfs接口可以方便地访问和管理kobject对象的属性和操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值