Linuxカーネルの共通マクロcontainer_of実は簡単です


開発プラットフォーム:Ubuntu 11.04
コンパイラ:gcc version 4.5.2(Ubuntu/Linaro 4.5.2-8 ubuntu 4)
 
    Container_ofはLinuxカーネルにおいてよく用いられるマクロであり、ある構造に含まれるポインタから構造そのもののポインタを得るために用いられ、一般的には構造体変数のあるメンバーのヘッダアドレスによってさらに構造体変数全体のヘッダアドレスを得る.
    Container_ofの定義は次のとおりです.
#define container_of(ptr, type, member) ({	    \
	const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
	(type *)( (char *)__mptr - offsetof(type,member) );})

実は文法は簡単ですが、いくつかのポインタの柔軟な応用で、2つのステップに分かれています.
まず、ptrと同じポインタ変数_(typeof(((type*)0)->member)で得られる一時的なデータ型を定義します.mptrを使用してptrの値を保存します.
ステップ2では、(char*)_mptrからmemberの構造体におけるオフセット量を減算すると,得られた値は構造体変数全体のヘッダアドレス(マクロ全体の戻り値がこのヘッダアドレス)である.
その中の文法の難点は、メンバーの構造体に対するオフセット量をどのように導くかです.
リスト1のような例で説明します.
/* linux-2.6.38.8/include/linux/compiler-gcc4.h */
#define __compiler_offsetof(a,b) __builtin_offsetof(a,b)

/* linux-2.6.38.8/include/linux/stddef.h */
#undef offsetof
#ifdef __compiler_offsetof
#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif

#include 

struct test_struct {
    int num;
    char ch;
    float fl;
};

int main(void)
{
    printf("offsetof(struct test_struct, num) = %d
", offsetof(struct test_struct, num)); printf("offsetof(struct test_struct, ch) = %d
", offsetof(struct test_struct, ch)); printf("offsetof(struct test_struct, fl) = %d
", offsetof(struct test_struct, fl)); return 0; }

説明、_builtin_offsetof(a,b)はGCCの内蔵関数であり,(size_t)&((TYPE*)0)->MEMBER)というコードと一致すると考えられる.
例出力結果:
offsetof(struct test_struct, num) = 0
offsetof(struct test_struct,  ch) = 4
offsetof(struct test_struct,  fl) = 8

その中でコードが理解しにくい点は,0アドレスを柔軟に運用していることである.&((struct test_struct*)0)->chのようなコードが理解しにくいと思う場合は、0アドレスに構造体変数struct test_が割り当てられていると仮定することができる.struct aは、その後、構造体ポインタ変数pを定義し、a(struct test_struct*p=&a)を指す.これにより、&p->chによってメンバーchのアドレスを得ることができる.aのヘッダアドレスは0 x 0であるため、メンバーchのヘッダアドレスは0 x 4である.
 
最後に、強制型変換(size_t)によってアドレス値を整数に変換する.
分析完了container_ofの定義は,次に2つの例を挙げてその使い方を体得する.
リスト2のような正しい例:
/* linux-2.6.38.8/include/linux/compiler-gcc4.h */
#define __compiler_offsetof(a,b) __builtin_offsetof(a,b)

/* linux-2.6.38.8/include/linux/stddef.h */
#undef offsetof
#ifdef __compiler_offsetof
#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif

/* linux-2.6.38.8/include/linux/kernel.h *
 * container_of - cast a member of a structure out to the containing structure
 * @ptr: the pointer to the member.
 * @type:	the type of the container struct this is embedded in.
 * @member:    the name of the member within the struct.
 *
 */
#define container_of(ptr, type, member) ({	    \
	const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
	(type *)( (char *)__mptr - offsetof(type,member) );})

#include 

struct test_struct {
    int num;
    char ch;
    float fl;
};

int main(void)
{
    struct test_struct init_test_struct = { 99, 'C', 59.12 };

    char *char_ptr = &init_test_struct.ch;

    struct test_struct *test_struct = container_of(char_ptr, struct test_struct, ch);
    
    printf(" test_struct->num = %d
test_struct->ch = %c
test_struct->fl = %f
", test_struct->num, test_struct->ch, test_struct->fl); return 0; }

例出力結果:
 test_struct->num = 99
 test_struct->ch = C
 test_struct->fl = 59.119999

リスト3のような不適切な例.
/* linux-2.6.38.8/include/linux/compiler-gcc4.h */
#define __compiler_offsetof(a,b) __builtin_offsetof(a,b)

/* linux-2.6.38.8/include/linux/stddef.h */
#undef offsetof
#ifdef __compiler_offsetof
#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif

/* linux-2.6.38.8/include/linux/kernel.h *
 * container_of - cast a member of a structure out to the containing structure
 * @ptr: the pointer to the member.
 * @type:	the type of the container struct this is embedded in.
 * @member:    the name of the member within the struct.
 *
 */
#define container_of(ptr, type, member) ({	    \
	const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
	(type *)( (char *)__mptr - offsetof(type,member) );})

#include 

struct test_struct {
    int num;
    char ch;
    float fl;
};

int main(void)
{
    char real_ch = 'A';
    char *char_ptr = &real_ch;

    struct test_struct *test_struct = container_of(char_ptr, struct test_struct, ch);

    printf(" char_ptr = %p  test_struct = %p

", char_ptr, test_struct); printf(" test_struct->num = %d
test_struct->ch = %c
test_struct->fl = %f
", test_struct->num, test_struct->ch, test_struct->fl); return 0; }

例出力結果:
 char_ptr = 0xbfb72d7f  test_struct = 0xbfb72d7b

 test_struct->num = -1511000897
 test_struct->ch = A
 test_struct->fl = 0.000000

なお、ここでは具体的な構造体変数はないため、メンバーnumおよびflの値は不確定である.