요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
=====================================================================
Everything you never wanted to know about kobjects, ksets, and ktypes
=====================================================================
:Author: Greg Kroah-Hartman <[email protected]>
:Last updated: December 19, 2007
Based on an original article by Jon Corbet for lwn.net written October 1,
2003 and located at https://lwn.net/Articles/51437/
Part of the difficulty in understanding the driver model - and the kobject
abstraction upon which it is built - is that there is no obvious starting
place. Dealing with kobjects requires understanding a few different types,
all of which make reference to each other. In an attempt to make things
easier, we'll take a multi-pass approach, starting with vague terms and
adding detail as we go. To that end, here are some quick definitions of
some terms we will be working with.
- A kobject is an object of type struct kobject. Kobjects have a name
and a reference count. A kobject also has a parent pointer (allowing
objects to be arranged into hierarchies), a specific type, and,
usually, a representation in the sysfs virtual filesystem.
Kobjects are generally not interesting on their own; instead, they are
usually embedded within some other structure which contains the stuff
the code is really interested in.
No structure should **EVER** have more than one kobject embedded within it.
If it does, the reference counting for the object is sure to be messed
up and incorrect, and your code will be buggy. So do not do this.
- A ktype is the type of object that embeds a kobject. Every structure
that embeds a kobject needs a corresponding ktype. The ktype controls
what happens to the kobject when it is created and destroyed.
- A kset is a group of kobjects. These kobjects can be of the same ktype
or belong to different ktypes. The kset is the basic container type for
collections of kobjects. Ksets contain their own kobjects, but you can
safely ignore that implementation detail as the kset core code handles
this kobject automatically.
When you see a sysfs directory full of other directories, generally each
of those directories corresponds to a kobject in the same kset.
We'll look at how to create and manipulate all of these types. A bottom-up
approach will be taken, so we'll go back to kobjects.
Embedding kobjects
==================
It is rare for kernel code to create a standalone kobject, with one major
exception explained below. Instead, kobjects are used to control access to
a larger, domain-specific object. To this end, kobjects will be found
embedded in other structures. If you are used to thinking of things in
object-oriented terms, kobjects can be seen as a top-level, abstract class
from which other classes are derived. A kobject implements a set of
capabilities which are not particularly useful by themselves, but are
nice to have in other objects. The C language does not allow for the
direct expression of inheritance, so other techniques - such as structure
embedding - must be used.
(As an aside, for those familiar with the kernel linked list implementation,
this is analogous as to how "list_head" structs are rarely useful on
their own, but are invariably found embedded in the larger objects of
interest.)
So, for example, the UIO code in ``drivers/uio/uio.c`` has a structure that
defines the memory region associated with a uio device::
struct uio_map {
struct kobject kobj;
struct uio_mem *mem;
};
If you have a struct uio_map structure, finding its embedded kobject is
just a matter of using the kobj member. Code that works with kobjects will
often have the opposite problem, however: given a struct kobject pointer,
what is the pointer to the containing structure? You must avoid tricks
(such as assuming that the kobject is at the beginning of the structure)
and, instead, use the container_of() macro, found in ``<linux/kernel.h>``::
container_of(ptr, type, member)
where:
* ``ptr`` is the pointer to the embedded kobject,
* ``type`` is the type of the containing structure, and
* ``member`` is the name of the structure field to which ``pointer`` points.
The return value from container_of() is a pointer to the corresponding
container type. So, for example, a pointer ``kp`` to a struct kobject
embedded **within** a struct uio_map could be converted to a pointer to the
**containing** uio_map structure with::
struct uio_map *u_map = container_of(kp, struct uio_map, kobj);
For convenience, programmers often define a simple macro for **back-casting**
kobject pointers to the containing type. Exactly this happens in the
earlier ``drivers/uio/uio.c``, as you can see here::
struct uio_map {
struct kobject kobj;
struct uio_mem *mem;
};
#define to_map(map) container_of(map, struct uio_map, kobj)
where the macro argument "map" is a pointer to the struct kobject in
question. That macro is subsequently invoked with::
struct uio_map *map = to_map(kobj);
Initialization of kobjects
==========================
Code which creates a kobject must, of course, initialize that object. Some
of the internal fields are setup with a (mandatory) call to kobject_init()::
void kobject_init(struct kobject *kobj, const struct kobj_type *ktype);
The ktype is required for a kobject to be created properly, as every kobject
must have an associated kobj_type. After calling kobject_init(), to
register the kobject with sysfs, the function kobject_add() must be called::
int kobject_add(struct kobject *kobj, struct kobject *parent,
const char *fmt, ...);
This sets up the parent of the kobject and the name for the kobject
properly. If the kobject is to be associated with a specific kset,
kobj->kset must be assigned before calling kobject_add(). If a kset is
associated with a kobject, then the parent for the kobject can be set to
NULL in the call to kobject_add() and then the kobject's parent will be the
kset itself.
As the name of the kobject is set when it is added to the kernel, the name
of the kobject should never be manipulated directly. If you must change
the name of the kobject, call kobject_rename()::
int kobject_rename(struct kobject *kobj, const char *new_name);
kobject_rename() does not perform any locking or have a solid notion of
what names are valid so the caller must provide their own sanity checking
and serialization.
There is a function called kobject_set_name() but that is legacy cruft and
is being removed. If your code needs to call this function, it is
incorrect and needs to be fixed.
To properly access the name of the kobject, use the function
kobject_name()::
const char *kobject_name(const struct kobject * kobj);
There is a helper function to both initialize and add the kobject to the
kernel at the same time, called surprisingly enough kobject_init_and_add()::
int kobject_init_and_add(struct kobject *kobj, const struct kobj_type *ktype,
struct kobject *parent, const char *fmt, ...);
The arguments are the same as the individual kobject_init() and
kobject_add() functions described above.
Uevents
=======
After a kobject has been registered with the kobject core, you need to
announce to the world that it has been created. This can be done with a
call to kobject_uevent()::
int kobject_uevent(struct kobject *kobj, enum kobject_action action);
Use the **KOBJ_ADD** action for when the kobject is first added to the kernel.
This should be done only after any attributes or children of the kobject
have been initialized properly, as userspace will instantly start to look
for them when this call happens.
When the kobject is removed from the kernel (details on how to do that are
below), the uevent for **KOBJ_REMOVE** will be automatically created by the
kobject core, so the caller does not have to worry about doing that by
hand.
Reference counts
================
One of the key functions of a kobject is to serve as a reference counter
for the object in which it is embedded. As long as references to the object
exist, the object (and the code which supports it) must continue to exist.
The low-level functions for manipulating a kobject's reference counts are::
struct kobject *kobject_get(struct kobject *kobj);
void kobject_put(struct kobject *kobj);
A successful call to kobject_get() will increment the kobject's reference
counter and return the pointer to the kobject.
When a reference is released, the call to kobject_put() will decrement the
reference count and, possibly, free the object. Note that kobject_init()
sets the reference count to one, so the code which sets up the kobject will
need to do a kobject_put() eventually to release that reference.
Because kobjects are dynamic, they must not be declared statically or on
the stack, but instead, always allocated dynamically. Future versions of
the kernel will contain a run-time check for kobjects that are created
statically and will warn the developer of this improper usage.
If all that you want to use a kobject for is to provide a reference counter
for your structure, please use the struct kref instead; a kobject would be
overkill. For more information on how to use struct kref, please see the
file Documentation/core-api/kref.rst in the Linux kernel source tree.
Creating "simple" kobjects
==========================
Sometimes all that a developer wants is a way to create a simple directory
in the sysfs hierarchy, and not have to mess with the whole complication of
ksets, show and store functions, and other details. This is the one
exception where a single kobject should be created. To create such an
entry, use the function::
struct kobject *kobject_create_and_add(const char *name, struct kobject *parent);
This function will create a kobject and place it in sysfs in the location
underneath the specified parent kobject. To create simple attributes
associated with this kobject, use::
int sysfs_create_file(struct kobject *kobj, const struct attribute *attr);
or::
int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp);
Both types of attributes used here, with a kobject that has been created
with the kobject_create_and_add(), can be of type kobj_attribute, so no
special custom attribute is needed to be created.
See the example module, ``samples/kobject/kobject-example.c`` for an
implementation of a simple kobject and attributes.
ktypes and release methods
==========================
One important thing still missing from the discussion is what happens to a
kobject when its reference count reaches zero. The code which created the
kobject generally does not know when that will happen; if it did, there
would be little point in using a kobject in the first place. Even
predictable object lifecycles become more complicated when sysfs is brought
in as other portions of the kernel can get a reference on any kobject that
is registered in the system.
The end result is that a structure protected by a kobject cannot be freed
before its reference count goes to zero. The reference count is not under
the direct control of the code which created the kobject. So that code must
be notified asynchronously whenever the last reference to one of its
kobjects goes away.
Once you registered your kobject via kobject_add(), you must never use
kfree() to free it directly. The only safe way is to use kobject_put(). It
is good practice to always use kobject_put() after kobject_init() to avoid
errors creeping in.
This notification is done through a kobject's release() method. Usually
such a method has a form like::
void my_object_release(struct kobject *kobj)
{
struct my_object *mine = container_of(kobj, struct my_object, kobj);
/* Perform any additional cleanup on this object, then... */
kfree(mine);
}
One important point cannot be overstated: every kobject must have a
release() method, and the kobject must persist (in a consistent state)
until that method is called. If these constraints are not met, the code is
flawed. Note that the kernel will warn you if you forget to provide a
release() method. Do not try to get rid of this warning by providing an
"empty" release function.
If all your cleanup function needs to do is call kfree(), then you must
create a wrapper function which uses container_of() to upcast to the correct
type (as shown in the example above) and then calls kfree() on the overall
structure.
Note, the name of the kobject is available in the release function, but it
must NOT be changed within this callback. Otherwise there will be a memory
leak in the kobject core, which makes people unhappy.
Interestingly, the release() method is not stored in the kobject itself;
instead, it is associated with the ktype. So let us introduce struct
kobj_type::
struct kobj_type {
void (*release)(struct kobject *kobj);
const struct sysfs_ops *sysfs_ops;
const struct attribute_group **default_groups;
const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
const void *(*namespace)(struct kobject *kobj);
void (*get_ownership)(struct kobject *kobj, kuid_t *uid, kgid_t *gid);
};
This structure is used to describe a particular type of kobject (or, more
correctly, of containing object). Every kobject needs to have an associated
kobj_type structure; a pointer to that structure must be specified when you
call kobject_init() or kobject_init_and_add().
The release field in struct kobj_type is, of course, a pointer to the
release() method for this type of kobject. The other two fields (sysfs_ops
and default_groups) control how objects of this type are represented in
sysfs; they are beyond the scope of this document.
The default_groups pointer is a list of default attributes that will be
automatically created for any kobject that is registered with this ktype.
ksets
=====
A kset is merely a collection of kobjects that want to be associated with
each other. There is no restriction that they be of the same ktype, but be
very careful if they are not.
A kset serves these functions:
- It serves as a bag containing a group of objects. A kset can be used by
the kernel to track "all block devices" or "all PCI device drivers."
- A kset is also a subdirectory in sysfs, where the associated kobjects
with the kset can show up. Every kset contains a kobject which can be
set up to be the parent of other kobjects; the top-level directories of
the sysfs hierarchy are constructed in this way.
- Ksets can support the "hotplugging" of kobjects and influence how
uevent events are reported to user space.
In object-oriented terms, "kset" is the top-level container class; ksets
contain their own kobject, but that kobject is managed by the kset code and
should not be manipulated by any other user.
A kset keeps its children in a standard kernel linked list. Kobjects point
back to their containing kset via their kset field. In almost all cases,
the kobjects belonging to a kset have that kset (or, strictly, its embedded
kobject) in their parent.
As a kset contains a kobject within it, it should always be dynamically
created and never declared statically or on the stack. To create a new
kset use::
struct kset *kset_create_and_add(const char *name,
const struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj);
When you are finished with the kset, call::
void kset_unregister(struct kset *k);
to destroy it. This removes the kset from sysfs and decrements its reference
count. When the reference count goes to zero, the kset will be released.
Because other references to the kset may still exist, the release may happen
after kset_unregister() returns.
An example of using a kset can be seen in the
``samples/kobject/kset-example.c`` file in the kernel tree.
If a kset wishes to control the uevent operations of the kobjects
associated with it, it can use the struct kset_uevent_ops to handle it::
struct kset_uevent_ops {
int (* const filter)(struct kobject *kobj);
const char *(* const name)(struct kobject *kobj);
int (* const uevent)(struct kobject *kobj, struct kobj_uevent_env *env);
};
The filter function allows a kset to prevent a uevent from being emitted to
userspace for a specific kobject. If the function returns 0, the uevent
will not be emitted.
The name function will be called to override the default name of the kset
that the uevent sends to userspace. By default, the name will be the same
as the kset itself, but this function, if present, can override that name.
The uevent function will be called when the uevent is about to be sent to
userspace to allow more environment variables to be added to the uevent.
One might ask how, exactly, a kobject is added to a kset, given that no
functions which perform that function have been presented. The answer is
that this task is handled by kobject_add(). When a kobject is passed to
kobject_add(), its kset member should point to the kset to which the
kobject will belong. kobject_add() will handle the rest.
If the kobject belonging to a kset has no parent kobject set, it will be
added to the kset's directory. Not all members of a kset do necessarily
live in the kset directory. If an explicit parent kobject is assigned
before the kobject is added, the kobject is registered with the kset, but
added below the parent kobject.
Kobject removal
===============
After a kobject has been registered with the kobject core successfully, it
must be cleaned up when the code is finished with it. To do that, call
kobject_put(). By doing this, the kobject core will automatically clean up
all of the memory allocated by this kobject. If a ``KOBJ_ADD`` uevent has been
sent for the object, a corresponding ``KOBJ_REMOVE`` uevent will be sent, and
any other sysfs housekeeping will be handled for the caller properly.
If you need to do a two-stage delete of the kobject (say you are not
allowed to sleep when you need to destroy the object), then call
kobject_del() which will unregister the kobject from sysfs. This makes the
kobject "invisible", but it is not cleaned up, and the reference count of
the object is still the same. At a later time call kobject_put() to finish
the cleanup of the memory associated with the kobject.
kobject_del() can be used to drop the reference to the parent object, if
circular references are constructed. It is valid in some cases, that a
parent objects references a child. Circular references _must_ be broken
with an explicit call to kobject_del(), so that a release functions will be
called, and the objects in the former circle release each other.
Example code to copy from
=========================
For a more complete example of using ksets and kobjects properly, see the
example programs ``samples/kobject/{kobject-example.c,kset-example.c}``,
which will be built as loadable modules if you select ``CONFIG_SAMPLE_KOBJECT``.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
문서 정보와 접근 방법
1-18kobject, kset, ktype에 관해 알고 싶지 않았던 모든 것 (Everything you never wanted to know about kobjects, ksets, and ktypes)
저자: Greg Kroah-Hartman <[email protected]>
마지막 갱신: 2007년 12월 19일
이 문서는 Jon Corbet이 2003년 10월 1일 lwn.net에 쓴 원문 기사 https://lwn.net/Articles/51437/ 를 바탕으로 합니다.
Driver model과 그 기반인 kobject abstraction을 이해하기 어려운 이유 중 하나는 분명한 출발점이 없기 때문입니다. Kobject를 다루려면 서로를 참조하는 여러 type을 이해해야 합니다. 설명을 쉽게 하기 위해 먼저 개략적인 용어를 소개하고 여러 차례에 걸쳐 세부 사항을 더하는 multi-pass 방식으로 진행합니다.
kobject, ktype, kset
19-47- kobject는 `struct kobject` type의 object입니다. 이름과 reference count, object를 hierarchy로 배치하는 parent pointer, 고유 type, 그리고 보통 sysfs virtual filesystem의 표현을 가집니다. Kobject 자체만으로는 대개 쓸모가 크지 않고 실제 관심 data를 담은 다른 structure 안에 embedded됩니다. 어떤 structure에도 kobject를 둘 이상 embedded해서는 절대 안 됩니다. 둘 이상이면 object reference counting이 반드시 어긋나 code에 bug가 생깁니다.
- ktype은 kobject를 embedded한 object의 type입니다. Kobject를 embedded하는 모든 structure에는 대응하는 ktype이 필요하며, ktype은 kobject를 만들고 없앨 때 수행할 동작을 제어합니다.
- kset은 kobject의 group입니다. 구성원은 같은 ktype일 수도, 서로 다른 ktype일 수도 있습니다. Kset은 kobject collection의 기본 container type이며 자체 kobject를 포함하지만 kset core code가 자동으로 관리하므로 이 구현 세부 사항은 무시해도 됩니다. 여러 directory가 든 sysfs directory를 보면 일반적으로 각 하위 directory가 같은 kset의 kobject 하나에 대응합니다.
이제 이 type을 생성하고 조작하는 방법을 bottom-up 방식으로 살펴보며, 먼저 kobject로 돌아갑니다.
kobject embedding
48-67kobject embedding (Embedding kobjects)
아래에서 설명할 중요한 예외 하나를 빼면 kernel code가 standalone kobject를 만드는 일은 드뭅니다. 대신 kobject는 더 크고 domain-specific한 object에 대한 접근을 제어하며 다른 structure 안에 embedded됩니다.
Object-oriented 관점에서 kobject는 다른 class가 파생되는 top-level abstract class로 볼 수 있습니다. 자체로는 특별히 유용하지 않지만 다른 object에 있으면 좋은 capability 집합을 구현합니다. C language는 inheritance를 직접 표현하지 못하므로 structure embedding 같은 기법을 사용해야 합니다.
Kernel linked list에 익숙하다면 `list_head` structure 자체는 거의 유용하지 않고, 관심 대상인 더 큰 object 안에 늘 embedded되는 방식과 같다고 생각할 수 있습니다.
UIO structure의 embedded kobject
68-75예를 들어 `drivers/uio/uio.c`의 UIO code는 uio device와 연결된 memory region을 아래 structure로 정의합니다.
struct uio_map {
struct kobject kobj;
struct uio_mem *mem;
};
container_of()로 포함 structure 찾기
76-97`struct uio_map`이 있으면 `kobj` member를 사용해 embedded kobject를 바로 찾을 수 있습니다. 반대로 `struct kobject` pointer에서 이를 포함하는 structure의 pointer를 찾을 때는 kobject가 structure 시작에 있다고 가정하는 식의 편법을 피하고 `<linux/kernel.h>`의 `container_of()` macro를 사용해야 합니다.
container_of(ptr, type, member)
인자는 다음 의미를 가집니다.
- `ptr`은 embedded kobject를 가리키는 pointer입니다.
- `type`은 이를 포함하는 structure의 type입니다.
- `member`는 `pointer`가 가리키는 structure field의 이름입니다.
`container_of()`는 대응하는 container type의 pointer를 반환합니다. `struct uio_map` 안에 embedded된 `struct kobject`를 가리키는 `kp`는 다음과 같이 이를 포함하는 `uio_map` pointer로 변환합니다.
struct uio_map *u_map = container_of(kp, struct uio_map, kobj);
back-casting helper macro
98-113편의를 위해 programmer는 kobject pointer를 이를 포함하는 type으로 back-casting하는 간단한 macro를 자주 정의합니다. 앞의 `drivers/uio/uio.c`도 다음과 같이 정확히 이 방식을 사용합니다.
struct uio_map {
struct kobject kobj;
struct uio_mem *mem;
};
#define to_map(map) container_of(map, struct uio_map, kobj)
여기서 macro 인자 `map`은 해당 `struct kobject`의 pointer이며, 이후 다음과 같이 macro를 호출합니다.
struct uio_map *map = to_map(kobj);
kobject 초기화와 등록
114-135kobject 초기화 (Initialization of kobjects)
Kobject를 만드는 code는 object를 초기화해야 합니다. 일부 internal field는 필수 호출인 `kobject_init()`으로 설정합니다.
void kobject_init(struct kobject *kobj, const struct kobj_type *ktype);
모든 kobject에는 연결된 `kobj_type`이 있어야 하므로 올바른 생성에 ktype이 필수입니다. `kobject_init()` 이후 kobject를 sysfs에 등록하려면 `kobject_add()`를 호출해야 합니다.
int kobject_add(struct kobject *kobj, struct kobject *parent,
const char *fmt, ...);
이 호출은 kobject의 parent와 name을 올바르게 설정합니다. 특정 kset에 연결하려면 `kobject_add()` 전에 `kobj->kset`을 지정해야 합니다. Kobject가 kset과 연결되어 있다면 `kobject_add()`에서 parent를 NULL로 둘 수 있고, 이때 kobject의 parent는 kset 자체가 됩니다.
kobject 이름 관리
136-155Kobject의 name은 kernel에 추가할 때 설정되므로 직접 조작해서는 안 됩니다. 반드시 바꿔야 한다면 `kobject_rename()`을 호출합니다.
int kobject_rename(struct kobject *kobj, const char *new_name);
`kobject_rename()`은 locking을 수행하지 않고 유효한 name에 대한 확고한 규칙도 두지 않으므로 caller가 자체 sanity checking과 serialization을 제공해야 합니다.
`kobject_set_name()`이라는 함수도 있지만 제거 중인 legacy cruft입니다. 이 함수를 호출해야 하는 code는 잘못된 것이므로 수정해야 합니다.
Kobject의 name에 올바르게 접근하려면 `kobject_name()`을 사용합니다.
const char *kobject_name(const struct kobject * kobj);
초기화와 등록을 한 번에 수행하기
156-165Kobject를 초기화하면서 동시에 kernel에 추가하는 helper인 `kobject_init_and_add()`도 있습니다.
int kobject_init_and_add(struct kobject *kobj, const struct kobj_type *ktype,
struct kobject *parent, const char *fmt, ...);
인자는 앞에서 설명한 개별 `kobject_init()` 및 `kobject_add()` 함수와 같습니다.
uevent 알림
166-185Uevents
Kobject를 kobject core에 등록한 뒤에는 생성 사실을 외부에 알려야 합니다. `kobject_uevent()` 호출로 이를 수행합니다.
int kobject_uevent(struct kobject *kobj, enum kobject_action action);
Kobject를 kernel에 처음 추가할 때는 `KOBJ_ADD` action을 사용합니다. 이 호출이 일어나면 userspace가 곧바로 attribute와 child를 찾기 시작하므로, 해당 요소가 모두 올바르게 초기화된 뒤에만 호출해야 합니다.
Kobject를 kernel에서 제거하면 kobject core가 `KOBJ_REMOVE` uevent를 자동으로 생성하므로 caller가 직접 만들 필요는 없습니다.
reference count
186-204Reference counts
Kobject의 핵심 기능 중 하나는 자신이 embedded된 object의 reference counter 역할을 하는 것입니다. Object에 대한 reference가 남아 있는 동안에는 object와 이를 지원하는 code가 계속 존재해야 합니다. Kobject reference count를 조작하는 low-level 함수는 다음과 같습니다.
struct kobject *kobject_get(struct kobject *kobj);
void kobject_put(struct kobject *kobj);
성공한 `kobject_get()` 호출은 kobject reference counter를 증가시키고 kobject pointer를 반환합니다.
Reference를 해제할 때 `kobject_put()`은 reference count를 감소시키며 object를 free할 수도 있습니다. `kobject_init()`은 reference count를 1로 설정하므로 kobject를 설정한 code는 언젠가 `kobject_put()`을 호출해 그 reference를 해제해야 합니다.
동적 할당과 kref 선택
205-215Kobject는 dynamic object이므로 static으로 선언하거나 stack에 두면 안 되고 항상 동적으로 할당해야 합니다. 향후 kernel은 static으로 생성한 kobject를 runtime에 검사하여 잘못 사용한 developer에게 경고할 예정입니다.
Structure에 reference counter만 필요하다면 kobject는 과하므로 대신 `struct kref`를 사용하십시오. 자세한 사용법은 Linux kernel source tree의 `Documentation/core-api/kref.rst`를 참조하십시오.
단순 kobject 생성
216-244단순 kobject 생성 (Creating "simple" kobjects)
개발자가 kset, show/store 함수와 다른 세부 사항을 모두 다루지 않고 sysfs hierarchy에 간단한 directory만 만들고 싶을 때가 있습니다. 이것이 standalone kobject를 생성해야 하는 유일한 예외입니다. 다음 함수를 사용합니다.
struct kobject *kobject_create_and_add(const char *name, struct kobject *parent);
이 함수는 kobject를 만들고 지정한 parent kobject 아래의 sysfs 위치에 배치합니다. 이 kobject와 연결된 간단한 attribute는 다음 중 하나로 만듭니다.
int sysfs_create_file(struct kobject *kobj, const struct attribute *attr);
또는 다음 group 함수를 사용합니다.
int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp);
`kobject_create_and_add()`로 만든 kobject에 쓰는 두 attribute 형식은 모두 `kobj_attribute` type일 수 있으므로 별도의 custom attribute를 만들 필요가 없습니다.
간단한 kobject와 attribute의 구현 예제는 module `samples/kobject/kobject-example.c`를 참조하십시오.
ktype과 해제 시점
245-267ktype과 release method (ktypes and release methods)
아직 다루지 않은 중요한 문제는 kobject reference count가 0이 될 때 어떤 일이 일어나는가입니다. Kobject를 만든 code는 일반적으로 그 시점을 알 수 없으며, 알 수 있다면 애초에 kobject를 사용할 이유가 거의 없습니다. Sysfs에 등록된 어떤 kobject든 kernel의 다른 부분이 reference를 얻을 수 있으므로 예측 가능한 object lifecycle도 더 복잡해집니다.
결국 kobject가 보호하는 structure는 reference count가 0이 되기 전에 free할 수 없습니다. Reference count는 생성 code가 직접 제어하지 않으므로 마지막 reference가 사라질 때 그 code에 asynchronous하게 통지해야 합니다.
`kobject_add()`로 kobject를 등록한 뒤에는 `kfree()`로 직접 해제해서는 절대 안 됩니다. 유일하게 안전한 방법은 `kobject_put()`이며, 오류 유입을 막기 위해 `kobject_init()` 뒤에는 항상 `kobject_put()`을 사용하는 습관이 좋습니다.
release() method
268-294이 통지는 kobject의 `release()` method를 통해 수행되며 보통 다음 형태입니다.
void my_object_release(struct kobject *kobj)
{
struct my_object *mine = container_of(kobj, struct my_object, kobj);
/* Perform any additional cleanup on this object, then... */
kfree(mine);
}
아무리 강조해도 지나치지 않은 점은 모든 kobject에 `release()` method가 있어야 하며, 그 method가 호출될 때까지 kobject가 일관된 상태로 계속 존재해야 한다는 것입니다. 이 제약을 지키지 않으면 code가 잘못된 것입니다. Kernel은 release method를 빠뜨리면 경고하며, 빈 release function을 제공해 경고만 없애려 해서는 안 됩니다.
Cleanup function이 `kfree()`만 호출하면 위 예제처럼 `container_of()`로 올바른 type으로 upcast한 뒤 전체 structure에 `kfree()`를 호출하는 wrapper function을 만들어야 합니다.
Release function에서 kobject의 name을 읽을 수는 있지만 callback 안에서 변경해서는 안 됩니다. 변경하면 kobject core에 memory leak이 생깁니다.
struct kobj_type
295-321`release()` method는 kobject 자체에 저장되지 않고 ktype과 연결됩니다. `struct kobj_type`은 다음과 같습니다.
struct kobj_type {
void (*release)(struct kobject *kobj);
const struct sysfs_ops *sysfs_ops;
const struct attribute_group **default_groups;
const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
const void *(*namespace)(struct kobject *kobj);
void (*get_ownership)(struct kobject *kobj, kuid_t *uid, kgid_t *gid);
};
이 structure는 특정 kobject type, 더 정확히는 이를 포함하는 object type을 기술합니다. 모든 kobject에는 연결된 `kobj_type` structure가 필요하며 `kobject_init()` 또는 `kobject_init_and_add()`를 호출할 때 그 pointer를 지정해야 합니다.
`struct kobj_type`의 `release` field는 해당 kobject type의 `release()` method를 가리킵니다. `sysfs_ops`와 `default_groups` field는 이 type의 object를 sysfs에 표현하는 방법을 제어하며 자세한 내용은 이 문서의 범위를 벗어납니다.
`default_groups` pointer는 이 ktype으로 등록되는 모든 kobject에 자동으로 생성할 default attribute의 list입니다.
kset의 역할
322-350ksets
Kset은 서로 연결하려는 kobject의 collection일 뿐입니다. 같은 ktype이어야 한다는 제한은 없지만 서로 다르다면 매우 주의해야 합니다.
Kset은 다음 기능을 수행합니다.
- Object group을 담는 bag 역할을 합니다. Kernel은 kset으로 "모든 block device" 또는 "모든 PCI device driver"를 추적할 수 있습니다.
- Kset은 연결된 kobject가 나타날 수 있는 sysfs subdirectory이기도 합니다. 모든 kset은 다른 kobject의 parent로 설정할 수 있는 kobject를 포함하며 sysfs hierarchy의 top-level directory도 이 방식으로 구성합니다.
- Kset은 kobject의 "hotplugging"을 지원하고 uevent event를 user space에 보고하는 방식에 영향을 줄 수 있습니다.
Object-oriented 관점에서 kset은 top-level container class입니다. 자체 kobject를 포함하지만 그 kobject는 kset code가 관리하므로 다른 user가 조작해서는 안 됩니다.
Kset은 child를 표준 kernel linked list에 보관합니다. Kobject는 `kset` field를 통해 자신을 포함하는 kset을 역참조합니다. 거의 모든 경우 kset 소속 kobject는 그 kset, 엄밀히는 kset에 embedded된 kobject를 parent로 가집니다.
kset 생성과 해제
351-370Kset도 내부에 kobject를 포함하므로 항상 동적으로 생성해야 하며 static 또는 stack에 선언해서는 안 됩니다. 새 kset은 다음 함수로 만듭니다.
struct kset *kset_create_and_add(const char *name,
const struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj);
Kset 사용을 마치면 다음 함수를 호출합니다.
void kset_unregister(struct kset *k);
이 호출은 kset을 sysfs에서 제거하고 reference count를 감소시킵니다. Reference count가 0이 되면 kset을 release합니다. 다른 reference가 남아 있을 수 있으므로 release는 `kset_unregister()`가 반환된 뒤에 일어날 수도 있습니다.
Kset 사용 예제는 kernel tree의 `samples/kobject/kset-example.c`에서 볼 수 있습니다.
kset uevent operation
371-391Kset이 연결된 kobject의 uevent operation을 제어하려면 `struct kset_uevent_ops`를 사용할 수 있습니다.
struct kset_uevent_ops {
int (* const filter)(struct kobject *kobj);
const char *(* const name)(struct kobject *kobj);
int (* const uevent)(struct kobject *kobj, struct kobj_uevent_env *env);
};
`filter` function은 특정 kobject의 uevent가 userspace로 나가지 않게 할 수 있습니다. 함수가 0을 반환하면 uevent를 보내지 않습니다.
`name` function은 uevent가 userspace에 보내는 기본 kset name을 대체하기 위해 호출합니다. 기본 name은 kset 자체의 name과 같지만 이 함수가 있으면 다른 name으로 바꿀 수 있습니다.
`uevent` function은 uevent를 userspace에 보내기 직전에 호출되며, uevent에 environment variable을 더 추가할 수 있게 합니다.
kobject를 kset에 추가하기
392-404별도의 추가 함수가 보이지 않지만 kobject를 kset에 넣는 작업은 `kobject_add()`가 처리합니다. `kobject_add()`에 kobject를 전달할 때 `kset` member가 소속될 kset을 가리키도록 하면 나머지는 함수가 처리합니다.
Kset 소속 kobject에 parent kobject가 설정되지 않았다면 kset directory에 추가됩니다. 모든 kset member가 반드시 그 directory에 있는 것은 아닙니다. Kobject를 추가하기 전에 명시적인 parent를 지정하면 kset에는 등록되지만 sysfs에서는 그 parent kobject 아래에 추가됩니다.
kobject 제거
405-428Kobject 제거 (Kobject removal)
Kobject core에 성공적으로 등록한 kobject는 사용을 마칠 때 `kobject_put()`으로 정리해야 합니다. 그러면 kobject core가 해당 kobject가 할당한 모든 memory를 자동으로 정리합니다. Object에 `KOBJ_ADD` uevent를 보냈다면 대응하는 `KOBJ_REMOVE` uevent도 보내며, 다른 sysfs housekeeping도 caller 대신 처리합니다.
Kobject를 두 단계로 삭제해야 한다면, 예를 들어 object를 없애야 하는 시점에 sleep할 수 없다면 `kobject_del()`을 호출해 먼저 sysfs 등록을 해제합니다. 그러면 kobject는 보이지 않게 되지만 아직 정리되지 않고 reference count도 그대로입니다. 나중에 `kobject_put()`을 호출해 연결된 memory 정리를 마칩니다.
Circular reference를 만들었다면 `kobject_del()`로 parent object에 대한 reference를 끊을 수 있습니다. Parent object가 child를 참조하는 것이 유효한 경우도 있지만, circular reference는 반드시 명시적인 `kobject_del()` 호출로 끊어야 release function이 호출되고 이전 cycle의 object들이 서로를 release할 수 있습니다.
복사해 사용할 예제 code
429-434복사해 사용할 예제 code (Example code to copy from)
Kset과 kobject를 올바르게 사용하는 더 완전한 예제는 `samples/kobject/{kobject-example.c,kset-example.c}`를 참조하십시오. `CONFIG_SAMPLE_KOBJECT`를 선택하면 이 example program을 loadable module로 빌드합니다.
요약과 해설
kobject.rst:1-434Kobject는 독립적인 domain object라기보다 다른 structure에 embedded되어 name, hierarchy, sysfs 표현과 reference lifetime을 제공하는 kernel 공통 기반입니다. 이를 포함하는 structure마다 동작과 release policy를 정의하는 ktype이 필요하며, 한 structure에는 kobject를 하나만 넣어야 합니다.
생성 경로는 `kobject_init()` 뒤 `kobject_add()`를 호출하거나 `kobject_init_and_add()`를 사용하는 것입니다. 등록 뒤에는 `KOBJ_ADD` uevent를 보내고, 모든 reference는 `kobject_get()`과 `kobject_put()`으로 관리합니다. 등록한 object를 `kfree()`로 직접 해제하면 안 되며 마지막 reference에서 ktype의 `release()`가 전체 container를 정리해야 합니다.
Kset은 관련 kobject의 collection이자 sysfs directory와 uevent policy의 단위입니다. `kset_uevent_ops`로 filter, name, environment를 조정할 수 있고, 제거 시에는 일반적으로 `kobject_put()`을 사용하며 two-stage cleanup이나 cycle 해소가 필요할 때 `kobject_del()`을 먼저 호출합니다.