1. 程式人生 > >sysfs檔案系統之 Attribute

sysfs檔案系統之 Attribute

Attributes
屬性

對於系統註冊的每個kobject,都會在sysfs檔案系統中建立一個對應的目錄.
attributes以普通檔案的形式為kobject匯出屬性到sysfs檔案系統.
attribute 的結構體定義如下:

struct attribute {
        char                    * name;
        struct module		*owner;
        mode_t                  mode;
};
int sysfs_create_file(struct kobject * kobj, const struct attribute * attr);
void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr);

單獨的attribute不包含讀寫屬性值的方法.subsystem鼓勵定義attribute自己的結構體和讀寫函式用來為kobject新增或刪除屬性.比如device_attribute定義為:

struct device_attribute {
	struct attribute	attr;
	ssize_t (*show)(struct device *dev, struct device_attribute *attr,
			char *buf);
	ssize_t (*store)(struct device *dev, struct device_attribute *attr,
			 const char *buf, size_t count);
};
int device_create_file(struct device *, const struct device_attribute *);
void device_remove_file(struct device *, const struct device_attribute *);

同時定義了巨集來幫助定義:

#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)

比如定義:

static DEVICE_ATTR(foo, S_IWUSR | S_IRUGO, show_foo, store_foo);

等效於:

static struct device_attribute dev_attr_foo = {
       .attr	= {
		.name = "foo",
		.mode = S_IWUSR | S_IRUGO,
		.show = show_foo,
		.store = store_foo,
	},
};

Subsystem相關的回撥函式

subsystem定義新的attribute型別時,它必須定義自己的一組讀寫函式來呼叫.

struct sysfs_ops {
        ssize_t (*show)(struct kobject *, struct attribute *, char *);
        ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t);
};

[subsystem應該先定義一個kobj_type結構體,裡面儲存了sysfs_ops 指標]

讀寫檔案的時候,sysfs呼叫相應的函式.這些函式轉換kobject和attribute的指標到相應的指標型別,然後呼叫相應的函式.舉例如下:

#define to_dev(obj) container_of(obj, struct device, kobj)
#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)

static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr,
                             char *buf)
{
        struct device_attribute *dev_attr = to_dev_attr(attr);
        struct device *dev = to_dev(kobj);
        ssize_t ret = -EIO;

        if (dev_attr->show)
                ret = dev_attr->show(dev, dev_attr, buf);
        if (ret >= (ssize_t)PAGE_SIZE) {
                print_symbol("dev_attr_show: %s returned bad count\n",
                                (unsigned long)dev_attr->show);
        }
        return ret;
}

sysfs檔案讀寫的呼叫流程

讀寫attribute資料

為了讀寫attribute資料,在定義attribute結構體的時候,show()或store()函式必須設定.這些函式型別應該類似一下定義:

ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
                 const char *buf, size_t count);

換句話說,這些函式應該帶object,attribute,buffer引數.
sysfs分配了一個大小為PAGE_SIZE的buffer並把它作為引數傳給這些函式.sysfs每次讀寫的時候都會呼叫這些函式.這需要在這些函式裡實現以下功能:

-讀,show()函式應該填充整個buffer
回憶一下attribute應該匯出一個值或為了高效匯出一組類似的值.
這樣方便使用者空間可以部分讀或隨意搜尋整個檔案.如果使用者空間搜尋
到0或呼叫pread在偏移地址為0的位置,show()函式會被再次呼叫重新填充buffer.

-寫,sysfs希望在首次寫呼叫的時候傳送完整的buffer.sysfs然後傳送完整的buffer到store()函式.
在寫sysfs檔案時,使用者空間程式應該先讀取整個檔案,修改,然後再寫入完整的buffer.

attribute函式在讀寫的時候應該使用同一個buffer.

其它注意事項:

-寫操作會呼叫show()函式重新載入,無論當前的檔案位置在哪裡

-buffer始終是PAGE_SIZE 位元組.i386,這個值是4096.

-show()函式會返回輸出到buffer的位元組數.也就是scnprintf()函式的返回值

-show()必須使用scnprintf().

-store()返回buffer使用的位元組數.如果整個buffer都被佔用了,則返回count值

-show()或store()應該返回error.如果錯的值出現,確保返回error.

-函式的引數object一直在記憶體通過內嵌的kobject sysfs的引用計數器.然而,
object表示的物理結構可能沒有提供.如果必要確保有方式檢測它.
(The object passed to the methods will be pinned in memory via sysfs
referencing counting its embedded object. However, the physical
entity (e.g. device) the object represents may not be present. Be
sure to have a way to check this, if necessary. )

非常簡單的device attribute實現如下:

static ssize_t show_name(struct device *dev, struct device_attribute *attr,
                         char *buf)
{
	return scnprintf(buf, PAGE_SIZE, "%s\n", dev->name);
}

static ssize_t store_name(struct device *dev, struct device_attribute *attr,
                          const char *buf, size_t count)
{
        snprintf(dev->name, sizeof(dev->name), "%.*s",
                 (int)min(count, sizeof(dev->name) - 1), buf);
	return count;
}
static DEVICE_ATTR(name, S_IRUGO, show_name, store_name);

(注意:實際的函式中不允許使用者空間設定device的名字)