1. 前言
sysfs是一個基于RAM的文件系統(tǒng),它和Kobject一起,可以將Kernel的數(shù)據(jù)結(jié)構(gòu)導(dǎo)出到用戶空間,以文件目錄結(jié)構(gòu)的形式,提供對這些數(shù)據(jù)結(jié)構(gòu)(以及數(shù)據(jù)結(jié)構(gòu)的屬性)的訪問支持。
sysfs具備文件系統(tǒng)的所有屬性,而本文主要側(cè)重其設(shè)備模型的特性,因此不會涉及過多的文件系統(tǒng)實現(xiàn)細(xì)節(jié),而只介紹sysfs在Linux設(shè)備模型中的作用和使用方法。具體包括:
sysfs和Kobject的關(guān)系
attribute的概念
sysfs的文件系統(tǒng)操作接口
2. sysfs和Kobject的關(guān)系
在"Linux設(shè)備模型_Kobject”文章中,有提到過,每一個Kobject,都會對應(yīng)sysfs中的一個目錄。因此在將Kobject添加到Kernel時,create_dir接口會調(diào)用sysfs文件系統(tǒng)的創(chuàng)建目錄接口,創(chuàng)建和Kobject對應(yīng)的目錄,相關(guān)的代碼如下:
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: }
3. attribute
3.1 attribute的功能概述
在sysfs中,為什么會有attribute的概念呢?其實它是對應(yīng)kobject而言的,指的是kobject的“屬性”。我們知道,
sysfs中的目錄描述了kobject,而kobject是特定數(shù)據(jù)類型變量(如struct device)的體現(xiàn)。因此kobject的屬性,就是這些變量的屬性。它可以是任何東西,名稱、一個內(nèi)部變量、一個字符串等等。而attribute,在sysfs文件系統(tǒng)中是以文件的形式提供的,即:kobject的所有屬性,都在它對應(yīng)的sysfs目錄下以文件的形式呈現(xiàn)。這些文件一般是可讀、寫的,而kernel中定義了這些屬性的模塊,會根據(jù)用戶空間的讀寫操作,記錄和返回這些attribute的值。
總結(jié)一下:所謂的attibute,就是內(nèi)核空間和用戶空間進(jìn)行信息交互的一種方法。例如某個driver定義了一個變量,卻希望用戶空間程序可以修改該變量,以控制driver的運行行為,那么就可以將該變量以sysfs attribute的形式開放出來。
Linux內(nèi)核中,attribute分為普通的attribute和二進(jìn)制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的基礎(chǔ)上,增加了read、write等函數(shù),因此它所生成的sysfs文件可以用任何方式讀寫。
說完基本概念,我們要問兩個問題:
Kernel怎么把a(bǔ)ttribute變成sysfs中的文件呢?
用戶空間對sysfs的文件進(jìn)行的讀寫操作,怎么傳遞給Kernel呢?
下面來看看這個過程。
3.2 attibute文件的創(chuàng)建
在linux內(nèi)核中,attibute文件的創(chuàng)建是由fs/sysfs/file.c中sysfs_create_file接口完成的,該接口的實現(xiàn)沒有什么特殊之處,大多是文件系統(tǒng)相關(guān)的操作,和設(shè)備模型沒有太多的關(guān)系,這里先略過不提。
3.3 attibute文件的read和write
看到3.1章節(jié)struct attribute的原型時,也許我們會犯嘀咕,該結(jié)構(gòu)很簡單啊,name表示文件名稱,mode表示文件模式,其它的字段都是內(nèi)核用于debug Kernel Lock的,那文件操作的接口在哪里呢?
不著急,我們?nèi)s/sysfs目錄下看看sysfs相關(guān)的代碼邏輯。
所有的文件系統(tǒng),都會定義一個struct file_operations變量,用于描述本文件系統(tǒng)的操作接口,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轉(zhuǎn)到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指針中取一個私有指針(注:大家可以稍微留一下心,私有數(shù)據(jù)的概念,在VFS中使用是非常普遍的),轉(zhuǎn)換為一個struct sysfs_buffer類型的指針,以此為參數(shù)(buffer),轉(zhuǎn)身就調(diào)用fill_read_buffer接口。
而fill_read_buffer接口,直接從buffer指針中取出一個struct sysfs_ops指針,調(diào)用該指針的show函數(shù),即完成了文件的read操作。
那么后續(xù)呢?當(dāng)然是由ops->show接口接著處理咯。而具體怎么處理,就是其它模塊(例如某個driver)的事了,sysfs不再關(guān)心(其實,Linux大多的核心代碼,都是只提供架構(gòu)和機(jī)制,具體的實現(xiàn),也就是苦力,留給那些碼農(nóng)吧!這就是設(shè)計的魅力)。
不過還沒完,這個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有關(guān)系。這個指針是從該attribute所從屬的kobject中拿的。再去看一下"Linux設(shè)備模型_Kobject”中ktype的定義,還真有一個struct sysfs_ops的指針。
我們注意一下14行的注釋以及其后代碼邏輯,如果從屬的kobject(就是attribute文件所在的目錄)沒有ktype,或者沒有ktype->sysfs_ops指針,是不允許它注冊任何attribute的!
經(jīng)過確認(rèn)后,sysfs_open_file從ktype中取出struct sysfs_ops指針,并在隨后的代碼邏輯中,分配一個struct sysfs_buffer類型的指針(buffer),并把struct sysfs_ops指針保存在其中,隨后(注意哦),把buffer指針交給file的private_data,隨后read/write等接口便可以取出使用。嗯!慣用伎倆!
順便看一下struct sysfs_ops吧,我想你已經(jīng)能夠猜到了。
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的邏輯,而二進(jìn)制類型的呢?也類似,去看看fs/sysfs/bin.c吧,這里也不說了。
講到這里,應(yīng)該已經(jīng)結(jié)束了,事實卻不是如此。上面read/write的數(shù)據(jù)流,只到kobject(也就是目錄)級別哦,而真正需要操作的是attribute(文件)啊!這中間一定還有一層轉(zhuǎn)換!確實,不過又交給其它模塊了。 下面我們通過一個例子,來說明如何轉(zhuǎn)換的。
4. sysfs在設(shè)備模型中的應(yīng)用總結(jié)
讓我們通過設(shè)備模型class.c中有關(guān)sysfs的實現(xiàn),來總結(jié)一下sysfs的應(yīng)用方式。
首先,在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: };
由前面章節(jié)的描述可知,所有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變量,而是通過一個自定義的數(shù)據(jù)結(jié)構(gòu),該數(shù)據(jù)結(jié)構(gòu)的一個成員是struct attribute類型的變量,并提供show和store回調(diào)函數(shù)。然后在該模塊ktype所對應(yīng)的struct sysfs_ops變量中,實現(xiàn)該本模塊整體的show和store函數(shù),并在被調(diào)用時,轉(zhuǎn)接到自定義數(shù)據(jù)結(jié)構(gòu)(struct class_attribute)中的show和store函數(shù)中。這樣,每個atrribute文件,實際上對應(yīng)到一個自定義數(shù)據(jù)結(jié)構(gòu)變量中了。
?
評論
查看更多