sysfs文件系统

一、sysfs

sysfs是用来向用户空间导出内核对象的一种文件系统,通过它,用户空间程序可以查看、甚至修改内核数据结构。该文件系统是基于内核数据结构kobject建立起来的,同时该文件系统的目录结构反映了相关内核数据结构的层次结构。由于kobject是组成设备模型的基本结构,因此sysfs也包括了系统中设备的信息,它提供了系统硬件的拓扑信息。
由于sysfs提供了访问、修改内核数据结构的一种手段,因而内核模块也可以通过该文件系统向用户空间导出接口用于访问、修改模块的参数。
在引入sysfs后,内核向用户到处接口的方式有/proc文件系统,sysfs文件系统,ioctl命令。
虽然sysfs的信息来自于kobject,但是kobject和sysfs的关联不是自动建立的,必须通过kobject_add才能把一个kobject添加到sysfs中。
由于内核中使用kobject的主要部件是硬件相关的部分,因而sysfs包含的最主要的内容就是硬件相关的内容,包括总线、设备、驱动程序等。Sysfs挂载点为/sys
rover$ ls /sys
block  bus  class  dev  devices  firmware  fs  hypervisor  kernel  module  power

1.1 sysfs节点

sysfs使用sysfs_dirent来表示一个目录或文件节点,其数据结构如下:
struct sysfs_dirent {
        atomic_t                s_count;
        atomic_t                s_active;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
        struct lockdep_map      dep_map;
#endif
        struct sysfs_dirent     *s_parent;
        const char              *s_name;


        struct rb_node          s_rb;


        union {
                struct completion       *completion;
                struct sysfs_dirent     *removed_list;
        } u;


        const void              *s_ns; /* namespace tag */
        unsigned int            s_hash; /* ns + name hash */
        union {
                struct sysfs_elem_dir           s_dir;
                struct sysfs_elem_symlink       s_symlink;
                struct sysfs_elem_attr          s_attr;
                struct sysfs_elem_bin_attr      s_bin_attr;
        };  


        unsigned short          s_flags;
        umode_t                 s_mode;
        unsigned int            s_ino;
        struct sysfs_inode_attrs *s_iattr;
};
其中
  • s_count:该sysfs节点的引用计数。它在创建一个新的sysfs_dirent时被设置为0。
  • s_active:该sysfs节点的活动应用计数。每当内核部件访问一个sysfs节点的内容sysfs_elem_*时就必须持有该引用计数,在访问完毕后要相应的更新该引用计数。之所以在引用计数之外再添加该参数是因为,如果没有该域,则只要用户打开了一个sysfs文件,就无法将其删除了。再增加了该计数后,只要它为负值,就不能再操作该sysfs节点了(这个限制添加在获取活动引用计数上,如果引用计数为负值,则无法获取引用计数,因此获取活动引用计数时,必须检查返回值), 当该节点的所有使用者都消失时,就可以删除该sysfs节点了。它在创建新的sysfs_dirent时被设置为1。
  • s_name:该sysfs_dirent的名字。
  • s_parent:指向父节点
  • s_rb:新的代码使用红黑树来管理同一目录下的子节点,同一目录下的兄弟节点被添加到一棵红黑树中,该域就是红黑树数据结构。红黑树的根为s_dir的children域。
  • s_ino:该sysfs节点在sysfs中的id。
  • s_flags:包含了该sysfs_dirent的类型信息和标志信息。类型指示了该sysfs_dirent是那种类型,类型包括SYSFS_DIR、SYSFS_KOBJ_ATTR、SYSFS_KOBJ_BIN_ATTR、SYSFS_KOBJ_LINK,每种类型对应于一个数据项结构,由于一个数据只能表示一种类型,因而这4种类型对应的数据结构被包括在一个枚举类型中。四种类型对应的数据结构分别为:
struct sysfs_elem_dir {
	struct kobject		*kobj;
	unsigned long		subdirs;
	/* children rbtree starts here and goes through sd->s_rb */
	struct rb_root		children;
};
struct sysfs_elem_symlink {
	struct sysfs_dirent	*target_sd;
};
struct sysfs_elem_attr {
	struct attribute	*attr;
	struct sysfs_open_dirent *open;
};
struct sysfs_elem_bin_attr {
	struct bin_attribute	*bin_attr;
	struct hlist_head	buffers;
};
struct sysfs_inode_attrs {
	struct iattr	ia_iattr;
	void		*ia_secdata;
	u32		ia_secdata_len;
};
sysfs使用dentry的d_fsdata来保存对应的sysfs_dirent,因此通过dentry就能找到对应的sysfs_dirent。
在sysfs节点的四种类型中:
  • 目录结构包含了该目录的红黑树的根节点信息以及该目录节点的kobject信息。
  • 连接结构包含了指向目标sysfs节点的指针信息
  • 而另外两种则包含了属性信息,即该节点对应的属性信息,属性信息对用户来说是最关键的。

1.2 sysfs节点属性

1.2.1属性信息定义如下:

struct attribute {
	const char		*name;
	umode_t			mode;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	struct lock_class_key	*key;
	struct lock_class_key	skey;
#endif
};
  • name为属性的名字,并且会作为文件名字出现在sysfs文件系统中
  • mode即该属性的访问属性,最终反映为sysfs文件系统中该属性文件的访问属性

1.2.2 二进制属性信息

其定义如下
struct bin_attribute {
	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);
};

该属性各个域的含义很明显。

二进制属性本身就包含了该属性的读写操作指针信息,它和常规的属性是不一样的,因而二进制属性文件必须显式创建,它不能作为kobject的默认属性。相关的API如下:

int sysfs_create_bin_file(struct kobject *kobj,  const struct bin_attribute *attr);
void sysfs_remove_bin_file(struct kobject *kobj, const struct bin_attribute *attr);

1.2.3 创建链接文件

sysfs中常见的一个操作是创建链接文件,其相关API如下:

int sysfs_create_link(struct kobject *kobj, struct kobject *target, const char *name);
void sysfs_delete_link(struct kobject *kobj, struct kobject *targ, const char *name);
这里只讨论了链接文件,二进制数形文件的创建,根据kobject的内容属性好像少了目录和常规属性的创建,常规属性的创建在kobject中有描述,而目录的创建则由kobject框架自动完成,不需要调用者关心。

1.3 自定义属性

如果一个内核部件需要定义自己的属性,实现方案也很简单,只需要定义如下的结构:
struct self_attribute{
    struct attributeattr;
    …
};
然后再sysfs需要属性时,将slef_attriute的attr提供给sysfs即可。这样sysfs仍可以使用自己所通用的struct attribute结构,而模块自己可以很容易的通过contanier_of宏得到自己所定义的自定义属性。

1.4 sysfs文件系统初始化

使用sysfs文件系统之前必须将其初始化并且挂载到系统中,该过程和proc文件系统完全类似。sysfs文件系统的的初始化主要完成:
  •  调用kmem_cache_create创建sysfs文件系统所使用的专用缓冲区
  •  调用register_filesystem注册sysfs文件系统,这里会提供proc文件系统自己的file_system_type,其中包括了用于mount的函数指针。在执行mount的时候会用到这些信息,并最终找到mount函数进行挂载操作。
在sysfs的mount函数中会调用sysfs_fill_super,它会
  • 给出sysfs文件系统超级块所需要的信息(比如文件系统的超级块操作函数指针,超级块大小等)。
  • 初始化根目录的inode,在这个过程中会指定与之对应的inode_operations和file_operations,有了这些信息后,VFS就可以在该文件系统上进行各种操作了(创建、删除、查找文件)。
  • 创建sysfs文件系统的根目录, 并且将dentry的d_fsdata指向根目录的sysfs_dirent结构sysfs_root。sysfs的每一个节点都对应一个dentry。
从上述描述可以看出,其和proc文件系统的初始化、以及mount是类似的。

设置sysfs的inode_operations和file_operations的工作是由sysfs_get_inode函数完成的,它最终调用函数sysfs_init_inode来完成这个工作。

1.5 sysfs文件节点删除

在删除一个sysfs节点时,sysfs会首先保证内核没有任何对sysfs内容的引用,这通过s_active来保证,然后在对该kobject的常规引用也全部消失后,就会真正删除该kobject。其流程大致为:

  1. 首先将一个特殊值SD_DEACTIVATED_BIAS加到s_active上,然后检查结果是否仍然为该值,如果结果仍然是该值,则和其它文件的删除类似,根据引用计数是否为0进行相应的处理,否则
  2. 在该kobject的u.completion上等待
能够结束该等待的就是对活动引用计数的put动作,在活动引用计数的put操作中,会进行检查,如果减1后的值为SD_DEACTIVATED_BIAS则就调用complete(sd->u.completion)结束该等待。

1.6 sysfs文件系统的操作

在文件操作中,inode文件操作相对比较简单,如果是目录,则提供有一个查找函数用于查找一个dentry,如果是普通文件,则提供有设置、查看文件属性的函数。相对比较负责的是实际的文件操作。
为了在内核和用户空间交互数据,sysfs还提供了一个数据结构来缓冲这种数据:
struct sysfs_buffer {
	size_t			count;
	loff_t			pos;
	char			* page;
	const struct sysfs_ops	* ops;
	struct mutex		mutex;
	int			needs_read_fill;
	int			event;
	struct list_head	list;
};
  • count:缓冲区中数据长度
  • pos:当前位置
  • page:指向存储数据的缓冲区
  • ops:实际的文件操作指针
  • needs_read_fill:缓冲区数据是否需要填充,在第一次读的时候肯定需要,如果在读之后没有写发生,则可以直接从缓冲区读而不需要重新填充。
  • list:关联到同一个sysfs的缓冲区列表
缓冲区列表与打开该sysfs的文件以及该sysfs的sysfs_dirent的关系如下所示:
 
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 *);
};

1.5.1打开文件

如果是普通属性文件,则在sysfs_get_inode中会将文件操作指针设置为sysfs_file_operations,其中包含了打开文件的函数sysfs_open_file,它会完成打开文件的操作。
在最新的代码中,要求所有的属性必须指定一个ktype,ktype中包含了一个struct sysfs_ops指针,用于保存相关联的文件操作指针。因此相关的代码逻辑比较简单:
  •  找到对应的ktype
  •  将sysfs_dirent的活动引用加1
  •  获得文件操作指针
  •  分配一个buffer,并对其进行初始化,同时将将文件操作赋给buffers
  •  如果是第一次打开该sysfs节点,则初始化一个用于poll的等待队列,否则仅将新的buffer添加到buffer的列表中
  •  将sysfs_dirent的活动引用减1

1.5.2读文件

如果是普通属性文件,则在sysfs_get_inode中会将文件操作指针设置为sysfs_file_operations,其中包含了打开文件的函数sysfs_read_file。该函数比较简单,它会
  • 找到打开文件对应的buffer
  • 查看是否需要填充
  • 如果需要填充,则
    • 将sysfs_dirent的活动引用加1
    • 调用buffer中的sysfs_ops中的show方法来将信息读取到buffer中
    • 将sysfs_dirent的活动引用减1
  •  如果不需要填充,则直接从buffer中读取

1.5.3写文件

如果是普通属性文件,则在sysfs_get_inode中会将文件操作指针设置为sysfs_file_operations,其中包含了打开文件的函数sysfs_write_file,该函数也很简单,它。
  • 找到打开文件对应的buffer
  • 首先将内容写到buffer中
  • 将sysfs_dirent的活动引用加1
  • 调用buffer的sysfs_ops中的store方法来完成最后的写
  • 将sysfs_dirent的活动引用减1
从上述描述可以看出,一个sysfs的实现者只需要将自己的真正的读写函数放到kobject的kobj_type的sysfs_ops中即可被调用到。
由于sysfs文件系统是基于kobject构建的,而kobject内嵌在其它各种内核数据结构中,相应的内核框架都已经包括了注册到sysfs中的代码,因而只需要遵循相应模块所属框架的规则即可完成向sysfs添加新的内容。

猜你喜欢

转载自blog.csdn.net/goodluckwhh/article/details/18706259
今日推荐