1. 前言
sysfs是一個基於RAM的文件系統,它和kobject一起,可以將kernel的數據結構導出到用戶空間,以文件目錄結構的形式,提供對這些數據結構(以及數據結構的屬性)的訪問支持。
sysfs具備文件系統的所有屬性,而本文主要側重其設備模型的特性,因此不會涉及過多的文件系統實現細節。
2. attribute
2.1 attribute的功能概述
在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,位於:include\linux\sysfs.h
詳細如下:
1 struct attribute { 2 const char *name; 3 umode_t mode; 4 #ifdef CONFIG_DEBUG_LOCK_ALLOC 5 bool ignore_lockdep:1; 6 struct lock_class_key *key; 7 struct lock_class_key skey; 8 #endif 9 } 10 11 struct bin_attribute { 12 struct attribute attr; 13 size_t size; 14 void *private; 15 ssize_t (*read)(struct file *, struct kobject *, struct bin_attribute *, 16 char *, loff_t, size_t); 17 ssize_t (*write)(struct file *,struct kobject *, struct bin_attribute *, 18 char *, loff_t, size_t); 19 int (*mmap)(struct file *, struct kobject *, struct bin_attribute *attr, 20 struct vm_area_struct *vma); 21 };
struct attribute為普通的attribute,使用該attribute生成的sysfs文件,只能用字符串的形式讀寫(后面會說為什么)。而struct bin_attribute在struct attribute的基礎上,增加了read、write等函數,因此它所生成的sysfs文件可以用任何方式讀寫。
說完基本概念,我們要問兩個問題:
kernel怎么把attribute變成sysfs中的文件呢?
用戶空間對sysfs的文件進行的讀寫操作,怎么傳遞給kernel呢?
下面來看看這個過程。
2.2 attibute文件的創建
在linux內核中,attibute文件的創建是由fs/sysfs/file.c中sysfs_create_file接口完成的,該接口的實現沒有什么特殊之處,大多是文件系統相關的操作,和設備模型關系不大,詳見:Linux設備驅動(7)sysfs詳解。
2.3 attibute文件的read和write
看到2.1章節struct attribute的原型時,也許我們會犯嘀咕,該結構很簡單啊,name表示文件名稱,mode表示文件模式,其它的字段都是內核用於debug Kernel Lock的,那文件操作的接口在哪里呢?
不着急,我們去fs/sysfs目錄下看看sysfs相關的代碼邏輯。
所有的文件系統,都會定義一個struct file_operations變量,用於描述本文件系統的操作接口,sysfs也不例外:
位於:fs\sysfs\file.c
1 const struct file_operations sysfs_file_operations = { 2 .read = sysfs_read_file, 3 .write = sysfs_write_file, 4 .llseek = generic_file_llseek, 5 .open = sysfs_open_file, 6 .release = sysfs_release, 7 .poll = sysfs_poll, 8 };
attribute文件的read操作,會由VFS轉到sysfs_file_operations的read(也就是sysfs_read_file)接口上,讓我們大概看一下該接口的處理邏輯。
1 struct sysfs_open_dirent { 2 atomic_t refcnt; 3 atomic_t event; 4 wait_queue_head_t poll; 5 struct list_head buffers; /* goes through sysfs_buffer.list */ 6 }; 7 8 struct sysfs_buffer { 9 size_t count; 10 loff_t pos; 11 char * page; 12 const struct sysfs_ops * ops; 13 struct mutex mutex; 14 int needs_read_fill; 15 int event; 16 struct list_head list; 17 };
再看sysfs_read_file 函數
1 /** 2 * sysfs_read_file - read an attribute. 3 * @file: file pointer. 4 * @buf: buffer to fill. 5 * @count: number of bytes to read. 6 * @ppos: starting offset in file. 7 * 8 * Userspace wants to read an attribute file. The attribute descriptor 9 * is in the file's ->d_fsdata. The target object is in the directory's 10 * ->d_fsdata. 11 * 12 * We call fill_read_buffer() to allocate and fill the buffer from the 13 * object's show() method exactly once (if the read is happening from 14 * the beginning of the file). That should fill the entire buffer with 15 * all the data the object has to offer for that attribute. 16 * We then call flush_read_buffer() to copy the buffer to userspace 17 * in the increments specified. 18 */ 19 20 static ssize_t 21 sysfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos) 22 { 23 struct sysfs_buffer * buffer = file->private_data; 24 ssize_t retval = 0; 25 26 mutex_lock(&buffer->mutex); 27 if (buffer->needs_read_fill || *ppos == 0) { 28 retval = fill_read_buffer(file->f_path.dentry,buffer); 29 if (retval) 30 goto out; 31 } 32 pr_debug("%s: count = %zd, ppos = %lld, buf = %s\n", 33 __func__, count, *ppos, buffer->page); 34 retval = simple_read_from_buffer(buf, count, ppos, buffer->page, 35 buffer->count); 36 out: 37 mutex_unlock(&buffer->mutex); 38 return retval; 39 }
read處理看着很簡單,sysfs_read_file從file指針中取一個私有指針(注:大家可以稍微留一下心,私有數據的概念,在VFS中使用是非常普遍的),轉換為一個struct sysfs_buffer類型的指針,以此為參數(buffer),轉身就調用fill_read_buffer接口。
1 /** 2 * fill_read_buffer - allocate and fill buffer from object. 3 * @dentry: dentry pointer. 4 * @buffer: data buffer for file. 5 * 6 * Allocate @buffer->page, if it hasn't been already, then call the 7 * kobject's show() method to fill the buffer with this attribute's 8 * data. 9 * This is called only once, on the file's first read unless an error 10 * is returned. 11 */ 12 static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer) 13 { 14 struct sysfs_dirent *attr_sd = dentry->d_fsdata; 15 struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; 16 const struct sysfs_ops * ops = buffer->ops; 17 int ret = 0; 18 ssize_t count; 19 20 if (!buffer->page) 21 buffer->page = (char *) get_zeroed_page(GFP_KERNEL); 22 if (!buffer->page) 23 return -ENOMEM; 24 25 /* need attr_sd for attr and ops, its parent for kobj */ 26 if (!sysfs_get_active(attr_sd)) 27 return -ENODEV; 28 29 buffer->event = atomic_read(&attr_sd->s_attr.open->event); 30 count = ops->show(kobj, attr_sd->s_attr.attr, buffer->page);//重要的點,最終call的是ops中show,ops就是struct sysfs_ops,ops哪里來的呢?就是sysfs_open_file時傳入的。 31 32 sysfs_put_active(attr_sd); 33 34 /* 35 * The code works fine with PAGE_SIZE return but it's likely to 36 * indicate truncated result or overflow in normal use cases. 37 */ 38 if (count >= (ssize_t)PAGE_SIZE) { 39 print_symbol("fill_read_buffer: %s returned bad count\n", 40 (unsigned long)ops->show); 41 /* Try to struggle along */ 42 count = PAGE_SIZE - 1; 43 } 44 if (count >= 0) { 45 buffer->needs_read_fill = 0; 46 buffer->count = count; 47 } else { 48 ret = count; 49 } 50 return ret; 51 }
而fill_read_buffer接口,直接從buffer指針中取出一個struct sysfs_ops指針,調用該指針的show函數,即完成了文件的read操作。
那么后續呢?當然是由ops->show接口接着處理咯。而具體怎么處理,就是其它模塊(例如某個driver)的事了,sysfs不再關心(其實,Linux大多的核心代碼,都是只提供架構和機制,具體的實現,也就是苦力,留給那些碼農吧!這就是設計的魅力)。
結構體定義位於:include\linux\sysfs.h
1 struct sysfs_ops { 2 ssize_t (*show)(struct kobject *, struct attribute *,char *); 3 ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t); 4 const void *(*namespace)(struct kobject *, const struct attribute *); 5 };
那struct sysfs_ops指針哪來的呢?就是從sysfs_open_file函數中賦值的。接着來看sysfs_file_open函數
1 struct sysfs_buffer { 2 size_t count; 3 loff_t pos; 4 char * page; 5 const struct sysfs_ops * ops; 6 struct mutex mutex; 7 int needs_read_fill; 8 int event; 9 struct list_head list; 10 };
1 static int sysfs_open_file(struct inode *inode, struct file *file) 2 { 3 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 4 struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; 5 struct sysfs_buffer *buffer; 6 const struct sysfs_ops *ops; 7 int error = -EACCES; 8 9 /* need attr_sd for attr and ops, its parent for kobj */ 10 if (!sysfs_get_active(attr_sd)) 11 return -ENODEV; 12 13 /* every kobject with an attribute needs a ktype assigned */ 14 if (kobj->ktype && kobj->ktype->sysfs_ops) 15 ops = kobj->ktype->sysfs_ops;//拿到sysfs_read_file()所需的ops指針,原來是從attribute所從屬kobj中的ktype中獲取的 16 else { 17 WARN(1, KERN_ERR "missing sysfs attribute operations for " 18 "kobject: %s\n", kobject_name(kobj)); 19 goto err_out;//如果從屬的kobject(就是attribute文件所在的目錄)沒有ktype,或者沒有ktype->sysfs_ops指針,是不允許它注冊任何attribute的 20 } 21 22 /* File needs write support. 23 * The inode's perms must say it's ok, 24 * and we must have a store method. 25 */ 26 if (file->f_mode & FMODE_WRITE) { 27 if (!(inode->i_mode & S_IWUGO) || !ops->store) 28 goto err_out; 29 } 30 31 /* File needs read support. 32 * The inode's perms must say it's ok, and we there 33 * must be a show method for it. 34 */ 35 if (file->f_mode & FMODE_READ) { 36 if (!(inode->i_mode & S_IRUGO) || !ops->show) 37 goto err_out; 38 } 39 40 /* No error? Great, allocate a buffer for the file, and store it 41 * it in file->private_data for easy access. 42 */ 43 error = -ENOMEM; 44 buffer = kzalloc(sizeof(struct sysfs_buffer), GFP_KERNEL); 45 if (!buffer) 46 goto err_out; 47 48 mutex_init(&buffer->mutex); 49 buffer->needs_read_fill = 1; 50 buffer->ops = ops;//再把ops賦給buffer 51 file->private_data = buffer;//最后把buffer賦值給file的私有數據,sysfs_read_file()就是從file的私有化數據中拿到的ops。 52 53 /* make sure we have open dirent struct */ 54 error = sysfs_get_open_dirent(attr_sd, buffer); 55 if (error) 56 goto err_free; 57 58 /* open succeeded, put active references */ 59 sysfs_put_active(attr_sd); 60 return 0; 61 62 err_free: 63 kfree(buffer); 64 err_out: 65 sysfs_put_active(attr_sd); 66 return error; 67 }
我們注意一下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等接口便可以取出使用。驅動的open函數也會用到類似機制。嗯!慣用伎倆!
位於:include\linux\sysfs.h
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 *); };
attribute文件的write過程和read類似,這里就不再多說。另外,上面只分析了普通attribute的邏輯,而二進制類型的呢?也類似,去看看fs/sysfs/bin.c吧,這里也不說了。
講到這里,應該已經結束了,事實卻不是如此。上面read/write的數據流,只到kobject(也就是目錄)級別哦,而真正需要操作的是attribute(文件)啊!這中間一定還有一層轉換!確實,不過又交給其它模塊了。 下面我們通過一個例子,來說明如何轉換的。
3. sysfs在設備模型中的應用總結
3.1 class
首先,在class.c中,定義了Class所需的ktype以及sysfs_ops類型的變量,讓我們通過設備模型class.c中有關sysfs的實現,來總結一下sysfs的應用方式。如下:
1 static const struct sysfs_ops class_sysfs_ops = { 2 .show = class_attr_show, 3 .store = class_attr_store, 4 }; 5 6 /* */ 7 static struct kobj_type class_ktype = { 8 .sysfs_ops = &class_sysfs_ops, 9 .release = class_release, 10 .child_ns_type = class_child_ns_type, 11 };
所有class_type的Kobject下面的attribute文件的讀寫操作,都會交給class_attr_show和class_attr_store兩個接口處理。以class_attr_show為例:
1 #define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr) 2 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 class_private *cp = to_class(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 struct class_attribute { 2 struct attribute attr; 3 ssize_t (*show)(struct class *class, struct class_attribute *attr, 4 char *buf); 5 ssize_t (*store)(struct class *class, struct class_attribute *attr, 6 const char *buf, size_t count); 7 }
因此,所有需要使用attribute的模塊,都不會直接定義struct attribute變量,而是通過一個自定義的數據結構,該數據結構的一個成員是struct attribute類型的變量,並提供show和store回調函數。然后在該模塊ktype所對應的struct sysfs_ops變量中,實現該本模塊整體的show和store函數,並在被調用時,轉接到自定義數據結構(struct class_attribute)中的show和store函數中。這樣,每個atrribute文件,實際上對應到一個自定義數據結構變量中了。
再看一下attribute,它本身並不具備任何的接口,所以它作為底層和kobject一樣,都要綁定在具體的實例(device或driver)中使用。
1 struct attribute { 2 const char *name; 3 struct module *owner; 4 mode_t mode; 5 #ifdef CONFIG_DEBUG_LOCK_ALLOC 6 struct lock_class_key *key; 7 struct lock_class_key skey; 8 #endif 9 };
struct class中包含device_attribute 和class_attribute
1 struct class { 2 const char *name; 3 struct module *owner; 4 5 struct class_attribute *class_attrs; 6 struct device_attribute *dev_attrs; 7 struct bin_attribute *dev_bin_attrs; 8 struct kobject *dev_kobj; 9 ... 10 }
3.2 devices
include\linux\device.h中,自定義一個定義屬性結構體,其中包含struct attribute類型。
1 /* interface for exporting device attributes */ 2 struct device_attribute { 3 struct attribute attr; 4 ssize_t (*show)(struct device *dev, struct device_attribute *attr, 5 char *buf); 6 ssize_t (*store)(struct device *dev, struct device_attribute *attr, 7 const char *buf, size_t count); 8 };
drivers\base\core.c 中給自定義的device屬性結構體賦值。
#define DEVICE_ATTR(_name, _mode, _show, _store) \ struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
static struct device_attribute devt_attr = __ATTR(dev, S_IRUGO, show_dev, NULL);
在device_add()中創建
1 int device_add(struct device *dev) 2 { 3 ... 4 if (MAJOR(dev->devt)) { 5 error = device_create_file(dev, &devt_attr); 6 if (error) 7 goto ueventattrError; 8 ... 9
3.3device_driver
同上,自定義屬性結構體driver_attribute ,中包含屬性結構體。
1 /* sysfs interface for exporting driver attributes */ 2 3 struct driver_attribute { 4 struct attribute attr; 5 ssize_t (*show)(struct device_driver *driver, char *buf); 6 ssize_t (*store)(struct device_driver *driver, const char *buf, 7 size_t count); 8 };
用下面宏給driver屬性結構體賦值
#define DRIVER_ATTR(_name, _mode, _show, _store) \ struct driver_attribute driver_attr_##_name = \ __ATTR(_name, _mode, _show, _store)
driver屬性結構體作為driver_create_file()的參數在drivers中使用。
1 int driver_create_file(struct device_driver *drv, 2 const struct driver_attribute *attr) 3 { 4 int error; 5 if (drv) 6 error = sysfs_create_file(&drv->p->kobj, &attr->attr); 7 else 8 error = -EINVAL; 9 return error; 10 }
3.4 bus
1 struct bus_attribute { 2 struct attribute attr; 3 ssize_t (*show)(struct bus_type *bus, char *buf); 4 ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count); 5 };
bus結構體bus_type中還分別包含device和driver的屬性結構體
1 struct bus_type { 2 const char *name; 3 const char *dev_name; 4 struct device *dev_root; 5 struct bus_attribute *bus_attrs; 6 struct device_attribute *dev_attrs; 7 struct driver_attribute *drv_attrs; 8 ... 9 }
bus屬性結構體作為bus_create_file()的參數在bus中使用。
1 int bus_create_file(struct bus_type *bus, struct bus_attribute *attr) 2 { 3 int error; 4 if (bus_get(bus)) { 5 error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr); 6 bus_put(bus); 7 } else 8 error = -EINVAL; 9 return error; 10 }
3.5 kobject
同上也是自定義了一個屬性結構體,include\linux\kobject.h
1 struct kobj_attribute { 2 struct attribute attr; 3 ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr, 4 char *buf); 5 ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr, 6 const char *buf, size_t count); 7 };
struct attribute作為kobj_attr_show()和kobj_attr_store()的參數,然后這兩個函數里面實際調用的是kobj_attribute的show、store函數。
1 /* default kobject attribute operations */ 2 static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr, 3 char *buf) 4 { 5 struct kobj_attribute *kattr; 6 ssize_t ret = -EIO; 7 8 kattr = container_of(attr, struct kobj_attribute, attr);//根據kobj_attribute中成員attribute拿到kobj_attribute 9 if (kattr->show) 10 ret = kattr->show(kobj, kattr, buf);//調用kobj_attribute中的show函數 11 return ret; 12 } 13 14 static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr, 15 const char *buf, size_t count) 16 { 17 struct kobj_attribute *kattr; 18 ssize_t ret = -EIO; 19 20 kattr = container_of(attr, struct kobj_attribute, attr); 21 if (kattr->store) 22 ret = kattr->store(kobj, kattr, buf, count); 23 return ret; 24 } 25 26 const struct sysfs_ops kobj_sysfs_ops = { 27 .show = kobj_attr_show, 28 .store = kobj_attr_store, 29 };
kobj_sysfs_ops有作為結構體kobject的成員,這樣kobj_attribute和kobject聯系了起來。
感謝博友分享
博友鏈接:https://blog.csdn.net/qq_16777851/java/article/details/81396047