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);
...
}
関連する用語
Linux
list_head
list_entry

コメントなどを投稿するフォームは、日本語対応時のみ表示されます


KisoDic通信用語の基礎知識検索システム WDIC Explorer Version 7.04a (27-May-2022)
Search System : Copyright © Mirai corporation
Dictionary : Copyright © WDIC Creators club