#sysfs是一種基於RAM的檔案系統,它與Kobject結合使用,能夠將Kernel的資料結構以及屬性匯出到使用者空間,以檔案目錄結構的形式提供對這些資料結構的存取支援。
#sysfs擁有檔案系統的所有屬性,但本文主要關注其在Linux設備模型中的特性。因此,不會涉及過多的檔案系統實作細節,而只介紹sysfs在設備模型中的作用和使用方法。具體包括:
在」Linux設備模型_Kobject」文章中,有提到過,每一個Kobject,都會對應sysfs中的一個目錄。因此在將Kobject加入Kernel時,create_dir接口會呼叫sysfs檔案系統的建立目錄接口,建立和Kobject對應的目錄,相關的程式碼如下:
1: /* lib/kobject.c, line 47 */ 2: static int create_dir(struct kobject *kobj) 3: { 4: int error = 0; 5: error = sysfs_create_dir(kobj); 6: if (!error) { 7: error = populate_dir(kobj); 8: if (error) 9: sysfs_remove_dir(kobj); 10: } 11: return error; 12: } 13: 14: /* fs/sysfs/dir.c, line 736 */ 15: ** 16: * sysfs_create_dir - create a directory for an object. 17: * @kobj: object we're creating directory for. 18: */ 19: int sysfs_create_dir(struct kobject * kobj) 20: { 21: enum kobj_ns_type type; 22: struct sysfs_dirent *parent_sd, *sd; 23: const void *ns = NULL; 24: int error = 0; 25: ... 26: }
在sysfs中,為什麼會有attribute的概念呢?其實它是對應kobject而言的,指的是kobject的「屬性」。我們知道,
sysfs中的目錄描述了kobject,而kobject是特定資料類型變數(如struct device)的體現。因此kobject的屬性,就是這些變數的屬性。它可以是任何東西,名稱、一個內部變數、一個字串等等。而attribute,在sysfs檔案系統中是以檔案的形式提供的,也就是:kobject的所有屬性,都在它對應的sysfs目錄下以檔案的形式呈現。這些檔案一般是可讀、寫入的,而kernel定義了這些屬性的模組,會根據使用者空間的讀寫操作,記錄和傳回這些attribute的值。
總結一下:所謂的attibute,就是核心空間和使用者空間進行資訊互動的一種方法。例如某個driver定義了一個變量,卻希望用戶空間程式可以修改該變量,以控制driver的運行行為,那麼就可以將該變量以sysfs attribute的形式開放出來。
Linux核心中,attribute分為普通的attribute和二進位attribute,如下:
1: /* include/linux/sysfs.h, line 26 */ 2: struct attribute { 3: const char *name; 4: umode_t mode; 5: #ifdef CONFIG_DEBUG_LOCK_ALLOC 6: bool ignore_lockdep:1; 7: struct lock_class_key *key; 8: struct lock_class_key skey; 9: #endif 10: }; 11: 12: /* include/linux/sysfs.h, line 100 */ 13: struct bin_attribute { 14: struct attribute attr; 15: size_t size; 16: void *private; 17: ssize_t (*read)(struct file *, struct kobject *, struct bin_attribute *, 18: char *, loff_t, size_t); 19: ssize_t (*write)(struct file *,struct kobject *, struct bin_attribute *, 20: char *, loff_t, size_t); 21: int (*mmap)(struct file *, struct kobject *, struct bin_attribute *attr, 22: struct vm_area_struct *vma); 23: };
struct attribute為普通的attribute,使用該attribute產生的sysfs文件,只能用字串的形式讀寫(後面會說為什麼)。而struct bin_attribute在struct attribute的基礎上,增加了read、write等函數,因此它所產生的sysfs檔案可以用任何方式讀寫。
說完基本概念,我們要問兩個問題:
Kernel怎麼把attribute變成sysfs中的檔案呢?
使用者空間對sysfs的檔案進行的讀寫操作,怎麼傳遞給Kernel呢?
下面來看看這個過程。
#在linux核心中,attibute檔案的建立是由fs/sysfs/file.c中sysfs_create_file介面完成的,該介面的實作沒有什麼特殊之處,大多是檔案系統相關的操作,和裝置模型沒有太多的關係,這裡先略過不提。
#看到3.1章節struct attribute的原型時,也許我們會犯嘀咕,該結構很簡單啊,name表示文件名稱,mode表示文件模式,其它的字段都是內核用於debug Kernel Lock的,那文件操作的介面在哪裡呢?
不急,我們去fs/sysfs目錄下看看sysfs相關的程式碼邏輯。
所有的檔案系統,都會定義一個struct file_operations變量,用來描述本檔案系統的操作接口,sysfs也不例外:
1: /* fs/sysfs/file.c, line 472 */ 2: const struct file_operations sysfs_file_operations = { 3: .read = sysfs_read_file, 4: .write = sysfs_write_file, 5: .llseek = generic_file_llseek, 6: .open = sysfs_open_file, 7: .release = sysfs_release, 8: .poll = sysfs_poll, 9: };
attribute檔案的read操作,會由VFS到sysfs_file_operations的read(也就是sysfs_read_file)介面上,讓我們大概看一下該介面的處理邏輯。
1: /* fs/sysfs/file.c, line 127 */ 2: static ssize_t 3: sysfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos) 4: { 5: struct sysfs_buffer * buffer = file->private_data; 6: ssize_t retval = 0; 7: 8: mutex_lock(&buffer->mutex); 9: if (buffer->needs_read_fill || *ppos == 0) { 10: retval = fill_read_buffer(file->f_path.dentry,buffer); 11: if (retval) 12: goto out; 13: } 14: ... 15: } 16: /* fs/sysfs/file.c, line 67 */ 17: static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer) 18: { 19: struct sysfs_dirent *attr_sd = dentry->d_fsdata; 20: struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; 21: const struct sysfs_ops * ops = buffer->ops; 22: ... 23: count = ops->show(kobj, attr_sd->s_attr.attr, buffer->page); 24: ... 25: }
“
read处理看着很简单,sysfs_read_file从file指针中取一个私有指针(注:大家可以稍微留一下心,私有数据的概念,在VFS中使用是非常普遍的),转换为一个struct sysfs_buffer类型的指针,以此为参数(buffer),转身就调用fill_read_buffer接口。
而fill_read_buffer接口,直接从buffer指针中取出一个struct sysfs_ops指针,调用该指针的show函数,即完成了文件的read操作。
那么后续呢?当然是由ops->show接口接着处理咯。而具体怎么处理,就是其它模块(例如某个driver)的事了,sysfs不再关心(其实,Linux大多的核心代码,都是只提供架构和机制,具体的实现,也就是苦力,留给那些码农吧!这就是设计的魅力)。
不过还没完,这个struct sysfs_ops指针哪来的?好吧,我们再看看open(sysfs_open_file)接口吧。
”
1: /* fs/sysfs/file.c, line 326 */ 2: static int sysfs_open_file(struct inode *inode, struct file *file) 3: { 4: struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 5: struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; 6: struct sysfs_buffer *buffer; 7: const struct sysfs_ops *ops; 8: int error = -EACCES; 9: 10: /* need attr_sd for attr and ops, its parent for kobj */ 11: if (!sysfs_get_active(attr_sd)) 12: return -ENODEV; 13: 14: /* every kobject with an attribute needs a ktype assigned */ 15: if (kobj->ktype && kobj->ktype->sysfs_ops) 16: ops = kobj->ktype->sysfs_ops; 17: else { 18: WARN(1, KERN_ERR "missing sysfs attribute operations for " 19: "kobject: %s\n", kobject_name(kobj)); 20: goto err_out; 21: } 22: 23: ... 24: 25: buffer = kzalloc(sizeof(struct sysfs_buffer), GFP_KERNEL); 26: if (!buffer) 27: goto err_out; 28: 29: mutex_init(&buffer->mutex); 30: buffer->needs_read_fill = 1; 31: buffer->ops = ops; 32: file->private_data = buffer; 33: ... 34: }
“
哦,原来和ktype有关系。这个指针是从该attribute所从属的kobject中拿的。再去看一下”Linux设备模型_Kobject”中ktype的定义,还真有一个struct sysfs_ops的指针。
我们注意一下14行的注释以及其后代码逻辑,如果从属的kobject(就是attribute文件所在的目录)没有ktype,或者没有ktype->sysfs_ops指针,是不允许它注册任何attribute的!
经过确认后,sysfs_open_file从ktype中取出struct sysfs_ops指针,并在随后的代码逻辑中,分配一个struct sysfs_buffer类型的指针(buffer),并把struct sysfs_ops指针保存在其中,随后(注意哦),把buffer指针交给file的private_data,随后read/write等接口便可以取出使用。嗯!惯用伎俩!
”
顺便看一下struct sysfs_ops吧,我想你已经能够猜到了。
1: /* include/linux/sysfs.h, line 124 */ 2: struct sysfs_ops { 3: ssize_t (*show)(struct kobject *, struct attribute *,char *); 4: ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t); 5: const void *(*namespace)(struct kobject *, const struct attribute *); 6: };
attribute文件的write过程和read类似,这里就不再多说。另外,上面只分析了普通attribute的逻辑,而二进制类型的呢?也类似,去看看fs/sysfs/bin.c吧,这里也不说了。
讲到这里,应该已经结束了,事实却不是如此。上面read/write的数据流,只到kobject(也就是目录)级别哦,而真正需要操作的是attribute(文件)啊!这中间一定还有一层转换!确实,不过又交给其它模块了。 下面我们通过一个例子,来说明如何转换的。
让我们通过设备模型class.c中有关sysfs的实现,来总结一下sysfs的应用方式。
首先,在class.c中,定义了Class所需的ktype以及sysfs_ops类型的变量,如下:
1: /* drivers/base/class.c, line 86 */ 2: static const struct sysfs_ops class_sysfs_ops = { 3: .show = class_attr_show, 4: .store = class_attr_store, 5: .namespace = class_attr_namespace, 6: }; 7: 8: static struct kobj_type class_ktype = { 9: .sysfs_ops = &class_sysfs_ops, 10: .release = class_release, 11: .child_ns_type = class_child_ns_type, 12: };
由前面章节的描述可知,所有class_type的Kobject下面的attribute文件的读写操作,都会交给class_attr_show和class_attr_store两个接口处理。以class_attr_show为例:
1: /* drivers/base/class.c, line 24 */ 2: #define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr) 3: 4: static ssize_t class_attr_show(struct kobject *kobj, struct attribute *attr, 5: char *buf) 6: { 7: struct class_attribute *class_attr = to_class_attr(attr); 8: struct subsys_private *cp = to_subsys_private(kobj); 9: ssize_t ret = -EIO; 10: 11: if (class_attr->show) 12: ret = class_attr->show(cp->class, class_attr, buf); 13: return ret; 14: }
该接口使用container_of从struct attribute类型的指针中取得一个class模块的自定义指针:struct class_attribute,该指针中包含了class模块自身的show和store接口。下面是struct class_attribute的声明:
1: /* include/linux/device.h, line 399 */ 2: struct class_attribute { 3: struct attribute attr; 4: ssize_t (*show)(struct class *class, struct class_attribute *attr, 5: char *buf); 6: ssize_t (*store)(struct class *class, struct class_attribute *attr, 7: const char *buf, size_t count); 8: const void *(*namespace)(struct class *class, 9: const struct class_attribute *attr); 10: };
因此,所有需要使用attribute的模块,都不会直接定义struct attribute变量,而是通过一个自定义的数据结构,该数据结构的一个成员是struct attribute类型的变量,并提供show和store回调函数。然后在该模块ktype所对应的struct sysfs_ops变量中,实现该本模块整体的show和store函数,并在被调用时,转接到自定义数据结构(struct class_attribute)
以上是詳解Linux設備模型(4)_sysfs的詳細內容。更多資訊請關注PHP中文網其他相關文章!