sysfsとkset
カーネルは管理するデバイスドライバのようなオブジェクトを、カテゴリ毎の階層的に管理しています。カテゴリ毎に統合することで同じ処理を共有化でき、階層化することで、親子関係で必要とするオブジェクトを管理することができます。そしてこの構造をユーザ空間にエクスポートしているのが/sysとなります。
/sys下のディレクトリがオブジェクトそのもので、配下のファイルがそのオブジェクトの属性となります。配下にディレクトリがある場合、それはそのオブジェクトの子オブジェクトということです。
この処理を管理するのが、kobjectです。オブジェクト毎の構造体内にkobjectを埋め込むことで、オブジェクト内に異なった処理をトランスペアレントに管理しています。
親子関係のオブジェクトを作成する場合、ksetが使われます。ksetそのものもkobjectをリストするkobjectで、ksetを登録すると言うことはkobjectを登録することと同じです。なお配下のオブジェクトはksetにリストされているので、子オブジェクト(ディレクトリ)の取得は、sysfsに依存することなく、ksetをヘッドとするリストを捜査することで行えます。
kobjectを登録すると、登録した/sys下のディレクトリにkobjectのディレクトリが作成され、その配下に、kobj_typeに設定するattrが、ファイルとして作成され、read/writeのコールバックとして、kobj_typeのコールバックがコールされます。
以下は、sysとkobj/ksetの関連性の理解するのに特化したサンプルです。
kset_create_and_add()でksetをkset_hogeディレクトリで、kernel_kobj下に作成します。kset_hogeオブジェクトのコールバック/属性が必要なら、第二引数(NULL)に、kobj_typeを設定します。
kobject_init_and_add()で、kobj(hoge)を登録するのですが、親オブジェクトの第二引数がNULLとしています。この時kobj->kset = hoge_ksetとするksetが親となります。なお、ksetの設定がないkobjectの登録は親となる第二引数を設定する必要があります。
サンプルでは、ret = kobject_init_and_add(kobj, &hoge_ktype, NULL, "%s", "hoge");と1つしかkset配下に登録していませんが、別のkobjを作成し、そのksetにhoge_ksetを設定して登録することで、随時kset配下のオブジェクトに登録されていくわけです。
/sys下のディレクトリがオブジェクトそのもので、配下のファイルがそのオブジェクトの属性となります。配下にディレクトリがある場合、それはそのオブジェクトの子オブジェクトということです。
この処理を管理するのが、kobjectです。オブジェクト毎の構造体内にkobjectを埋め込むことで、オブジェクト内に異なった処理をトランスペアレントに管理しています。
親子関係のオブジェクトを作成する場合、ksetが使われます。ksetそのものもkobjectをリストするkobjectで、ksetを登録すると言うことはkobjectを登録することと同じです。なお配下のオブジェクトはksetにリストされているので、子オブジェクト(ディレクトリ)の取得は、sysfsに依存することなく、ksetをヘッドとするリストを捜査することで行えます。
kobjectを登録すると、登録した/sys下のディレクトリにkobjectのディレクトリが作成され、その配下に、kobj_typeに設定するattrが、ファイルとして作成され、read/writeのコールバックとして、kobj_typeのコールバックがコールされます。
以下は、sysとkobj/ksetの関連性の理解するのに特化したサンプルです。
kset_create_and_add()でksetをkset_hogeディレクトリで、kernel_kobj下に作成します。kset_hogeオブジェクトのコールバック/属性が必要なら、第二引数(NULL)に、kobj_typeを設定します。
kobject_init_and_add()で、kobj(hoge)を登録するのですが、親オブジェクトの第二引数がNULLとしています。この時kobj->kset = hoge_ksetとするksetが親となります。なお、ksetの設定がないkobjectの登録は親となる第二引数を設定する必要があります。
#include <linux/kobject.h> #include <linux/string.h> #include <linux/sysfs.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/init.h> MODULE_LICENSE("GPL"); static char sysbuff_aaa[64]; static char sysbuff_bbb[64]; static ssize_t hoge_show(struct kobject *kobj, struct attribute *attr, char *buf) { if (!strcmp(attr->name, "aaa")) { sprintf(buf, "from aaa %s", sysbuff_aaa); } else { sprintf(buf, "from bbb %s", sysbuff_bbb); } return strlen(buf); } static ssize_t hoge_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t len) { if (!strcmp(attr->name, "aaa")) { strcpy(sysbuff_aaa, buf); } else { strcpy(sysbuff_bbb, buf); } return len; } static const struct sysfs_ops hoge_sysfs_ops = { .show = hoge_show, .store = hoge_store, }; static void hoge_release(struct kobject *kobj) { kfree(kobj); } static struct attribute hoge_attr1 = { "aaa", 0666, }; static struct attribute hoge_attr2 = { "bbb", 0666, }; static struct attribute *hoge_attrs[] = { &hoge_attr1, &hoge_attr2, NULL, }; static struct kobj_type hoge_ktype = { .sysfs_ops = &hoge_sysfs_ops, .release = hoge_release, .default_attrs = hoge_attrs, }; static struct kset *hoge_kset; struct kobject *kobj; static int __init hoge_init(void) { int ret; hoge_kset = kset_create_and_add("kset_hoge", NULL, kernel_kobj); if (!hoge_kset) return -ENOMEM; kobj = kzalloc(sizeof(*kobj), GFP_KERNEL); if (!kobj) return -EINVAL; kobj->kset = hoge_kset; ret = kobject_init_and_add(kobj, &hoge_ktype, NULL, "%s", "hoge"); return 0; } static void __exit hoge_exit(void) { kobject_put(kobj); kset_unregister(hoge_kset); } module_init(hoge_init); module_exit(hoge_exit);
[root@localhost lkm]# insmod sysfs.ko [root@localhost lkm]# ls -l /sys/kernel/kset_hoge/hoge/ 合計 0 -rw-rw-rw- 1 root root 4096 10月 13 06:17 aaa -rw-rw-rw- 1 root root 4096 10月 13 06:17 bbb [root@localhost lkm]# echo hogehoge1 > /sys/kernel/kset_hoge/hoge/aaa [root@localhost lkm]# echo hogehoge2 > /sys/kernel/kset_hoge/hoge/bbb [root@localhost lkm]# cat /sys/kernel/kset_hoge/hoge/aaa from aaa hogehoge1 [root@localhost lkm]# cat /sys/kernel/kset_hoge/hoge/bbb from bbb hogehoge2
補足
struct kobject/struct attributeをオブジェクト/アトリビュートのstructのメンバー(コンテナ)とすることで、任意の実装することができ、またstruct attributeコンテナにコールバックを設定することで、属性(ファイル)毎に指定したコールバック関数をコールすることができます。ただし、.sysfs_opsの.show/.storeコールバック関数内で、所定のattrコールバック関数がコールされるようにする必要があります。サンプルでは、ret = kobject_init_and_add(kobj, &hoge_ktype, NULL, "%s", "hoge");と1つしかkset配下に登録していませんが、別のkobjを作成し、そのksetにhoge_ksetを設定して登録することで、随時kset配下のオブジェクトに登録されていくわけです。
追記
下記はカーネルソースにインプリメントされている、実務にそったksetの使用例です。/sts/kerneにkset_sampleを作成し、kobjとしてfoo/bar/bazを、その属性としてそれぞれにfoo/bar/bazを作成します。kobjをfoo_objに埋め込み、attributeをfoo_attributeに埋め込んで、sysfs_opのコールバック.show/.storeで、引数はkobj/attr故、container_of()マクロでfoo_obj/foo_attributeを取得、foo_attributeに設定されている独自のコールバック関数をコールします。#include <linux/kobject.h> #include <linux/string.h> #include <linux/sysfs.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/init.h> struct foo_obj { struct kobject kobj; int foo; int baz; int bar; }; #define to_foo_obj(x) container_of(x, struct foo_obj, kobj) struct foo_attribute { struct attribute attr; ssize_t (*show)(struct foo_obj *foo, struct foo_attribute *attr, char *buf); ssize_t (*store)(struct foo_obj *foo, struct foo_attribute *attr, const char *buf, size_t count); }; #define to_foo_attr(x) container_of(x, struct foo_attribute, attr) static ssize_t foo_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct foo_attribute *attribute; struct foo_obj *foo; attribute = to_foo_attr(attr); foo = to_foo_obj(kobj); if (!attribute->show) return -EIO; return attribute->show(foo, attribute, buf); } static ssize_t foo_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t len) { struct foo_attribute *attribute; struct foo_obj *foo; attribute = to_foo_attr(attr); foo = to_foo_obj(kobj); if (!attribute->store) return -EIO; return attribute->store(foo, attribute, buf, len); } static const struct sysfs_ops foo_sysfs_ops = { .show = foo_attr_show, .store = foo_attr_store, }; static void foo_release(struct kobject *kobj) { struct foo_obj *foo; foo = to_foo_obj(kobj); kfree(foo); } static ssize_t foo_show(struct foo_obj *foo_obj, struct foo_attribute *attr, char *buf) { return sprintf(buf, "%d\n", foo_obj->foo); } static ssize_t foo_store(struct foo_obj *foo_obj, struct foo_attribute *attr, const char *buf, size_t count) { sscanf(buf, "%du", &foo_obj->foo); return count; } static struct foo_attribute foo_attribute = __ATTR(foo, 0666, foo_show, foo_store); static ssize_t b_show(struct foo_obj *foo_obj, struct foo_attribute *attr, char *buf) { int var; if (strcmp(attr->attr.name, "baz") == 0) var = foo_obj->baz; else var = foo_obj->bar; return sprintf(buf, "%d\n", var); } static ssize_t b_store(struct foo_obj *foo_obj, struct foo_attribute *attr, const char *buf, size_t count) { int var; sscanf(buf, "%du", &var); if (strcmp(attr->attr.name, "baz") == 0) foo_obj->baz = var; else foo_obj->bar = var; return count; } static struct foo_attribute baz_attribute = __ATTR(baz, 0666, b_show, b_store); static struct foo_attribute bar_attribute = __ATTR(bar, 0666, b_show, b_store); static struct attribute *foo_default_attrs[] = { &foo_attribute.attr, &baz_attribute.attr, &bar_attribute.attr, NULL, /* need to NULL terminate the list of attributes */ }; static struct kobj_type foo_ktype = { .sysfs_ops = &foo_sysfs_ops, .release = foo_release, .default_attrs = foo_default_attrs, }; static struct kset *example_kset; static struct foo_obj *foo_obj; static struct foo_obj *bar_obj; static struct foo_obj *baz_obj; static struct foo_obj *create_foo_obj(const char *name) { struct foo_obj *foo; int retval; foo = kzalloc(sizeof(*foo), GFP_KERNEL); if (!foo) return NULL; foo->kobj.kset = example_kset; retval = kobject_init_and_add(&foo->kobj, &foo_ktype, NULL, "%s", name); if (retval) { kobject_put(&foo->kobj); return NULL; } kobject_uevent(&foo->kobj, KOBJ_ADD); return foo; } static void destroy_foo_obj(struct foo_obj *foo) { kobject_put(&foo->kobj); } static int __init example_init(void) { example_kset = kset_create_and_add("kset_example", NULL, kernel_kobj); if (!example_kset) return -ENOMEM; foo_obj = create_foo_obj("foo"); if (!foo_obj) goto foo_error; bar_obj = create_foo_obj("bar"); if (!bar_obj) goto bar_error; baz_obj = create_foo_obj("baz"); if (!baz_obj) goto baz_error; return 0; baz_error: destroy_foo_obj(bar_obj); bar_error: destroy_foo_obj(foo_obj); foo_error: return -EINVAL; } static void __exit example_exit(void) { destroy_foo_obj(baz_obj); destroy_foo_obj(bar_obj); destroy_foo_obj(foo_obj); kset_unregister(example_kset); } module_init(example_init); module_exit(example_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Greg Kroah-Hartman <greg@kroah.com>");実行結果
[root@localhost kset_example]# pwd /sys/kernel/kset_example [root@localhost kset_example]# ls -l 合計 0 drwxr-xr-x 2 root root 0 10月 17 15:11 bar drwxr-xr-x 2 root root 0 10月 17 15:11 baz drwxr-xr-x 2 root root 0 10月 17 15:11 foo [root@localhost kset_example]# ls -l bar/ 合計 0 -rw-rw-rw- 1 root root 4096 10月 17 15:12 bar -rw-rw-rw- 1 root root 4096 10月 17 15:12 baz -rw-rw-rw- 1 root root 4096 10月 17 15:12 foo [root@localhost kset_example]# ls -l baz/ 合計 0 -rw-rw-rw- 1 root root 4096 10月 17 15:12 bar -rw-rw-rw- 1 root root 4096 10月 17 15:12 baz -rw-rw-rw- 1 root root 4096 10月 17 15:12 foo [root@localhost kset_example]# ls -l foo/ 合計 0 -rw-rw-rw- 1 root root 4096 10月 17 15:13 bar -rw-rw-rw- 1 root root 4096 10月 17 15:13 baz -rw-rw-rw- 1 root root 4096 10月 17 15:13 foo [root@localhost kset_example]# echo 11 bar/ba bar baz [root@localhost kset_example]# echo 11 > bar/bar [root@localhost kset_example]# echo 12 > bar/baz [root@localhost kset_example]# echo 13 > bar/foo [root@localhost kset_example]# echo 21 > baz/bar [root@localhost kset_example]# echo 22 > baz/baz [root@localhost kset_example]# echo 23 > baz/foo [root@localhost kset_example]# echo 31 > foo/bar [root@localhost kset_example]# echo 32 > foo/baz [root@localhost kset_example]# echo 33 > foo/foo [root@localhost kset_example]# cat bar/bar 11 [root@localhost kset_example]# cat bar/baz 12 [root@localhost kset_example]# cat bar/foo 13 [root@localhost kset_example]# cat baz/bar 21 [root@localhost kset_example]# cat baz/baz 22 [root@localhost kset_example]# cat baz/foo 23 [root@localhost kset_example]# cat foo/bar 31 [root@localhost kset_example]# cat foo/baz 32 [root@localhost kset_example]# cat foo/foo 33