sysfsとkobject
Rev.3を表示中。最新版はこちら。
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内で設定されいる属性の名前でkobject_example以下にファイルを作成する。sysfsへの登録処理はこれだけである。あとはattribute構造体で設定したコールバック関数が必要に応じてコールされることになる。
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); };
実は/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関数がある。この場合個々の属性でのコールバック関数は不要となる。サンプルでの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, }