1. 程式人生 > >Linux核心中的常用巨集container_of其實很簡單

Linux核心中的常用巨集container_of其實很簡單

    開發平臺:Ubuntu11.04

    編 譯器:gcc version 4.5.2 (Ubuntu/Linaro4.5.2-8ubuntu4)

    Container_of在Linux核心中是一個常用的巨集,用於從包含在某個結構中的指標獲得結構本身的指標,通俗地講就是通過結構體變數中某個成員的首地址進而獲得整個結構體變數的首地址。

    Container_of的定義如下: 

#define container_of(ptr, type, member) ({	    \
	const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
	(type *)( (char *)__mptr - offsetof(type,member) );})

    其實它的語法很簡單,只是一些指標的靈活應用,它分兩步:

    第一步,首先定義一個臨時的資料型別(通過typeof( ((type *)0)->member )獲得)與ptr相同的指標變數__mptr,然後用它來儲存ptr的值。

    第二步,用(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 <stdio.h>

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

int main(void)
{
    printf("offsetof(struct test_struct, num) = %d\n", 
			offsetof(struct test_struct, num));
    
    printf("offsetof(struct test_struct,  ch) = %d\n", 
			offsetof(struct test_struct, ch));
    
    printf("offsetof(struct test_struct,  fl) = %d\n", 
			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的首地址為0x0,所以成員ch的首地址為0x4。

 

    最後通過強制型別轉換(size_t)把一個地址值轉換為一個整數。

    分析完container_of的定義,接下來舉兩個例子來體會一下它的使用方法。

    正確的例子,如清單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 <stdio.h>

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\n test_struct->ch = %c\n test_struct->fl = %f\n", 
	    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 <stdio.h>

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\n\n", char_ptr, test_struct);

    printf(" test_struct->num = %d\n test_struct->ch = %c\n test_struct->fl = %f\n", 
	    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的值是不確定的。