container_of
読み:コンテナー-オブ
外語:container_of
Linuxで使われている、要素が含まれる構造体などのアドレスを得るためのマクロ。list_entryと同じ動きをするが、用途が違うため別名となっている。
書式
マクロなので、使用するにはincludeが必要。
#include <linux/kernel.h>
定義
kernel.h
container_ofは、linux/kernel.hで次のように定義されている。
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
typeof演算子はGCCの拡張機能で、既存の項目と同じ型の新しい項目を生成するものである。
offsetofは、linux/stddef.hで次のように定義されている。
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
とても分かりにくいが、ptrから、構造体の先頭からmemberへのオフセットを引き算することで、構造体の先頭アドレスを得ている。
別の手法
Androidなどでも、他のディレクトリのヘッダーファイルでは、独自の定義をしていることもある。
最もシンプルな実装方法は次の通り。例えば次のような定義をするヘッダーファイルがあった。
#define list_entry(link, type, member) \
((type *)((char *)(link)-(unsigned long)(&((type *)0)->member)))
typeof演算子などを使わずとも、この方法だけで充分目的を達することが可能ということだろう。
但し、Linuxのcontainer_ofの実装でわざわざtypeof演算子を使っていると言うことは、これでは求められない特殊な型が存在するのかもしれない。
特徴
用法
list_head関係でよく使われるが、それに限らない。構造体のメンバーから構造体自体を得る場面では、汎用的に利用できる。
一般的な、struct list_head *から元の構造体を取り出す場合では、次の引数を設定する。
- 第1引数にstruct list_head *
- 第2引数に取り出したい構造体名
- 第3引数に取り出したい構造体の中にあるstruct list_headの要素名
こうすると、取り出したい構造体へのポインターが出て来るのである。
計算によって構造体のアドレスを求めるため、struct list_head *は、構造体の中のどこにあってもよい。構造体の中に入っていれば、それだけでその構造体を取り出せる。
用例
container_ofの使い方は、機能が全く同じである、list_entryと同様である。
struct list_head *p;
struct inner_st *pin;
struct outer_st *pout;
list_for_each(p, &list)
{
pin = list_entry(p, struct inner_st, node);
pout = container_of(pin, struct outer_st, sin);
if (pout->outdata == 123)
{
/* 処理 */
}
}
リスト中で関連付けられている中から、outer_st.outdata==123の要素を抽出する処理も、このように書くことができる。
他の用例
例えば、AndroidのLEDデバイスドライバーの処理では、struct led_classdev *という構造体が使われるが、実際には、ハードウェアに応じた構造体の中にこの構造体を含んで用いている。
もしQUALCOMMのPM8xxx系PMICを用いている一般的なスマートフォンであるなら、struct pm8xxx_led_dataの中にstruct led_classdev *が入っている。
点灯や消灯などの要求がある時は、関数がstruct led_classdev *を引数として呼び出される。その関数内ではcontainer_ofを用いてstruct pm8xxx_led_dataを取り出すのである。
例えば、android/kernel/drivers/leds/leds-pm8xxx.cでは、次のようにして元の構造体を得ている。
static void pm8xxx_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct pm8xxx_led_data *led;
led = container_of(led_cdev, struct pm8xxx_led_data, cdev);
...
}
再検索