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 *から元の構造体を取り出す場合では、次の引数を設定する。
 こうすると、取り出したい構造体へのポインターが出て来るのである。
 計算によって構造体のアドレスを求めるため、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);
...
}

再検索