sysfsは仮想的なファイルシステムとして、カーネル空間のリソースをユーザ空間に公開するもの、procfsで実現できないからsysfsというわけでなく、実際のところsysfsとprocfsの機能的な違いはない。実装の違いで言うなら、procfsのコールバックの処理内容とそのprocファイルの関連性はないのだが、sysfsではそのsysファイルとコールバックの処理に関連性を持たせているということか。
本サンプルはカーネルのソースとして組み込まれているものを、kobject(kernel object)の構造を理解しやすくするために一部変更したものである。
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/module.h>
#include <linux/init.h>
static struct kobject *example_kobj;
static int foo;
static int baz;
static int bar;
static ssize_t foo_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
return sprintf(buf, "%s:%d\n", attr->attr.name, foo);
}
static ssize_t foo_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
sscanf(buf, "%du", &foo);
return count;
}
static ssize_t baz_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
return sprintf(buf, "%s:%d\n", attr->attr.name, baz);
}
static ssize_t baz_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
sscanf(buf, "%du", &baz);
return count;
}
static ssize_t bar_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
return sprintf(buf, "%s:%d\n", attr->attr.name, bar);
}
static ssize_t bar_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
sscanf(buf, "%du", &bar);
return count;
}
static struct kobj_attribute foo_attribute = __ATTR(foo, 0666, foo_show, foo_store);
static struct kobj_attribute baz_attribute = __ATTR(baz, 0666, baz_show, baz_store);
static struct kobj_attribute bar_attribute = __ATTR(bar, 0666, bar_show, bar_store);
static struct attribute *attrs[] = {
&foo_attribute.attr,
&baz_attribute.attr,
&bar_attribute.attr,
NULL, /* need to NULL terminate the list of attributes */
};
static struct attribute_group attr_group = {
.attrs = attrs,
};
static int example_init(void)
{
int retval;
example_kobj = kobject_create_and_add("kobject_example", kernel_kobj);
if (!example_kobj)
return -ENOMEM;
retval = sysfs_create_group(example_kobj, &attr_group);
if (retval)
kobject_put(example_kobj);
return retval;
}
static void example_exit(void)
{
kobject_put(example_kobj);
}
module_init(example_init);
module_exit(example_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Greg Kroah-Hartman <greg@kroah.com>");
本サンプルをカーネルに組み込むと下記のようなファイルが/sys以下に作成される。
[root@localhost ~]# ls /sys/kernel/kobject_example/
bar baz foo
kobject_create_and_add関数でkobject_exampleという名前のkobject構造体を作成する。kobject_example各メンバーの初期設定と同時にkobject_exampleというディレクトリが作成される。kernel_kobj引数はその位置を指定するもので、この場合/sys/kernel/下に作成されることになる。kernel_kobjは静的変数でkenelと言う名のkobjectで、システム起動時に/sys下に作成されている。なおこの引数をNULLにすると/sys下に作成されることになる。
kobj_attribute構造体をまとめたattr_group構造体を引数として、sysfs_create_group関数をコールすると、attrのメンバーnameで設定されいる属性の名前で、kobject_exampleディレクトリ下(注)にその名前のファイルを作成する。sysfsへの登録処理はこれだけで、あとは設定したコールバック関数が必要に応じてコールされる。
#define __ATTR(_name,_mode,_show,_store) { \
.attr = {.name = __stringify(_name), .mode = _mode }, \
.show = _show, \
.store = _store, \
}
struct kobj_attribute {
struct attribute attr;
ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr,
char *buf);
ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count);
};
struct attribute {
const char *name;
struct module *owner;
mode_t mode;
};
実は/sys/kernel/kobject_sample/fooを読み書きすると、直接foo_show、foo_storeコールバック関数呼ばれているのでない。sysfsとしてはこれらにかかるコールバック関数は、そのkobjectのkobject->ktypeに設定されるコールバック関数がコールされる。サンプルでkobjectを作成/登録しているkobject_create_and_add関数には、このktypeを設定する引数がない。この場合kobject_create_and_add関数はデフォルトのdynamic_kobj_ktypeというktypeが設定される。そこでの表示処理はkobj_attr_show関数がコールされて、その引数から該当する属性のコールバックを2段がまえで呼び出すようになっている。
なんか回りくどい処理で、直接kobjectのコールバック関数で処理させれば?と。kobject作成時、ユーザの定義したコールバック関数を引数として渡し定するというkobject_init関数というのがある。この場合個々の属性でのコールバック関数は不要で、1つのコールバック関数内でattr->nameでどの属性値を見ながら処理をすることになる。サンプルのsysfs_create_group関数で属性ファイルを作っているが、この場合、sysfs_create_file関数と属性ごとに作成していくことになる。属性値が少ない場合こちらの方が簡易かも?
static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct kobj_attribute *kattr;
ssize_t ret = -EIO;
kattr = container_of(attr, struct kobj_attribute, attr);
if (kattr->show)
ret = kattr->show(kobj, kattr, buf);
return ret;
}
なお、list_head構造体のようにkobject構造体を管理したい構造体に組み込むことで、オブジェクトライクな実装も可能である。またkobject群をまとめたkset構造体というものがる。kset自体もkobjectでkset名のディレクトリが作成され、その配下にkobjectが現れるのだが、ksetの使い方については改めてということで。
注
attr_group構造体にnameを設定すると、そのディレクトリが作成されその配下に属性ファイルが作られることになる。下記の例では
[root@localhost ~]# ls /sys/kernel/kobject_example/hogehoge/
bar baz foo
static struct attribute_group attr_group = {
.name = "hogehoge",
.attrs = attrs,
}