Reference: https://lwn.net/Articles/54651/
Kernel Object
kobject
는 kernel 내부에서 구조체를 관리하기 위한 도구이다.
이 도구는 독립적으로는 잘 쓰이지 않으며, 주로(99.9%) 구조체에 임베딩 되어 쓰인다.
자신을 임베딩한 구조체를 관리하기 위한 기능을 여러가지 내장하고 있는데 다음과 같은 기능을 내장하고 있다.
// kobject를 임베딩한 struct foo
struct foo {
struct kobject kobj;
...
};
- 계층화
- 부모를 표현하기 위한
kobject->parent
- 내가 소속된
kset
(optional)
- 부모를 표현하기 위한
- 참조 카운트 관리
kobject_get()
- ref count 1 증가kobject_put()
- ref conut 1 감소
kobject
의 해제, 그리고kobject
디바이스 파일 RW를 담당하는ktype
- 디바이스 파일은
/sys
하위항목에 생성된다.
- 디바이스 파일은
kobject
를 그룹으로 관리하기 위한kset
1
즉, kobject
는 구조체를 /sys
하위에 노출 시키면서, 구조체를 계층적으로 관리할 수 있게 도와주는 도구라고 보면 된다.
kobject의 생성
kobject
는 kobject_init()
으로 생성할 수 있다.
void kobject_init(struct kobject* kobj, struct kobj_type* ktype)
위에서 볼 수 있듯이, 임의의 kobj
를 인자로 받고, ktype
으로 kobj
의 관리 정보들을 결정한다.
초기화된 kobject
를 sysfs에 등록시키기 위해서는 kobject_add()
를 이용한다.
void kobject_add(struct kobject* kobj, struct kobject* parent, const char* fmt, ...)
이 때, sysfs root에 등록시키려면 parent == NULL
을 변수로 하여 호출한다.
ktype
ktype
은 kobject
의 해제, 그리고 kobject
와 관련된 디바이스 파일 RW를 담당한다.
struct kobj_type {
void (*release)(struct kobject* kobj);
const struct sysfs_ops* sysfs_ops;
struct attribute** default_attrs;
...
}
release
는 kobject
가 임베딩 된 struct의 해제를 담당한다.
struct foo {
struct kobject kobj;
...
};
void releasea_foo(struct kobject* kboj) {
struct foo* p = container_of(kobj, struct foo, kobj);
kfree(p)
}
static struct kobj_type foo_ktype {
.release = release_foo,
.default_attrs = attrs_foo,
.sysfs_ops = sysfs_ops_foo,
};
위에서 알 수 있듯이, struct foo
에 kobject
가 임베딩 되어 있고, release_foo()
를 콜하여 struct foo
를 메모리 해제할 수 있다.
release_foo()
는 release
에 펑션 포인터로 등록되어 있는데, kref
의 ref count가 0이 되면 자동으로 호출된다.
default_attrs
는 struct attribute*
를 배열로 가지고 있다.
kobject
가 kobject_add()
에 의해 등록되면 내부에서 struct attribute*
배열 크기 만큼 sysfs_create_file()
를 호출한다.
struct attribute
내부에 name과 access mode를 멤버변수가 있다. 이 멤버변수들을 이용해 sysfs_create_file()
으로 디바이스 파일을 /sys/
하위에 생성한다.
sysfs_ops
는 struct attribute
에 의해 생성된 디바이스 파일을 접근(read/write)할 때, 동작할 핸들러를 지정한다.
struct sysfs_ops {
ssize_t (*show)(...);
ssize_t (*store)(...);
}
show()
는 struct attribute
에 의해 생성된 디바이스 파일들을 읽을 때, 호출되며 store()
는 디바이스 파일을 쓸 때 호출된다.
서로 다른 디바이스 파일을 읽고 쓸 때, 똑같은 struct sysfs_ops
가 호출된다.
하지만 상식적으로 당연히 다른 디바이스 파일을 읽으면 다른 struct sysfs_ops
가 호출되어야 한다.
이 때문에 다음과 같이 트릭을 쓰게 된다.
struct custom_attr {
struct attribute attr;
ssize_t (*show)(...);
ssize_t (*store)(...);
}
static ssize_t sysfs_show(struct kobject* kobj, struct attribute* attr, char* buf) {
struct custom_attr* cattr = container_of(attr, struct custom_attr, attr)
if (likely(cattr->show))
return cattr->show(buf);
return -EIO;
}
위에서 sysfs_show()
는 struct sysfs_ops
에 등록된 common operation이다.
attribute
에 해당하는 custom operation을 호출하기 위해, attribute
를 포함하는 구조체에 원하는 handler를 등록해놓고 container_of()
를 통해 핸들러를 접근하면 된다.
kref
참조 카운트를 관리하는 변수이다.
만약 구조체의 스마트한 해제기능만을 사용하기 위해서는 kobject
를 쓰지 말고, kref
만을 포함하여 쓰는것이 맞다.
kobject_init()
는 kobject
를 만들 시, ref count를 1로 셋팅한다.
kobject_get()
은 ref count를 1 올리며, kobject_put()
은 ref count를 1 내린다.
만약 kobject
의 ref count가 0이 되면 ktype->release()
를 호출한다.
일반적으로 자식이 생기면 부모의 kobject
의 ref count가 증가되며, 자식이 해제되면 ref count가 감소된다.
kset
kobject
를 그룹으로 관리하기 위한 구조체이며, 자체적으로 kobject
를 포함하고 있다.
계층 구조를 만드려면 kobject
내부에 있는 kset
을 이용한다.
kobject->kset
을 설정하고 kobject_add()
를 호출하면 kset
리스트에 kobject
가 등록되며, kobject->parent
는 kset->kobj
가 된다.
kobj->kset = kset_create(...);
kobject_add(kobj, ...);
// kobject_add에 parent가 NULL이면, kobj->parent == kset->kobj가 되며, kset 내부 리스트에 kobj가 등록된다.
// 단, kobject_add()에 parent가 있다면 kobj->parent == parent이다. (kset 내부 리스트에는 kobj가 정상적으로 등록된다.)
-
kobject->kset
를 설정하고kobject_add()
를 호출하면 자동으로 현 kobject ↩