歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux綜合 >> Linux內核

Linux內核中的常用宏container_of其實很簡單

    開發平台:Ubuntu11.04

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

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

    Container_of的定義如下: 

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

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

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

    第二步,用(char *)__mptr減去member在結構體中的偏移量,得到的值就是整個結構體變量的首地址(整個宏的返回值就是這個首地址)。

    其中的語法難點就是如何得出成員相對結構體的偏移量?

    通過例子說明,如清單1: 

  1. /* linux-2.6.38.8/include/linux/compiler-gcc4.h */  
  2. #define __compiler_offsetof(a,b) __builtin_offsetof(a,b)   
  3.   
  4. /* linux-2.6.38.8/include/linux/stddef.h */  
  5. #undef offsetof   
  6. #ifdef __compiler_offsetof   
  7. #define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)   
  8. #else   
  9. #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)   
  10. #endif   
  11.   
  12. #include <stdio.h>   
  13.   
  14. struct test_struct {  
  15.     int num;  
  16.     char ch;  
  17.     float fl;  
  18. };  
  19.   
  20. int main(void)  
  21. {  
  22.     printf("offsetof(struct test_struct, num) = %d\n",   
  23.             offsetof(struct test_struct, num));  
  24.       
  25.     printf("offsetof(struct test_struct,  ch) = %d\n",   
  26.             offsetof(struct test_struct, ch));  
  27.       
  28.     printf("offsetof(struct test_struct,  fl) = %d\n",   
  29.             offsetof(struct test_struct, fl));  
  30.       
  31.     return 0;  
  32. }  

    說明,__builtin_offsetof(a,b)是GCC的內置函數,可認為它的實現與((size_t) &((TYPE *)0)->MEMBER)這段代碼是一致的。

    例子輸出結果:

  1. offsetof(struct test_struct, num) = 0  
  2. offsetof(struct test_struct,  ch) = 4  
  3. offsetof(struct test_struct,  fl) = 8  

    其中代碼難以理解的地方就是它靈活地運用了0地址。如果覺得&( (structtest_struct *)0 )->ch這樣的代碼不好理解,那麼我們可以假設在0地址分配了一個結構體變量structtest_struct a,然後定義結構體指針變量p並指向a(struct test_struct *p = &a),如此我們就可以通過&p->ch獲得成員ch的地址。由於a的首地址為0x0,所以成員ch的首地址為0x4。

 

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

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

    正確的例子,如清單2: 

  1. /* linux-2.6.38.8/include/linux/compiler-gcc4.h */  
  2. #define __compiler_offsetof(a,b) __builtin_offsetof(a,b)   
  3.   
  4. /* linux-2.6.38.8/include/linux/stddef.h */  
  5. #undef offsetof   
  6. #ifdef __compiler_offsetof   
  7. #define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)   
  8. #else   
  9. #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)   
  10. #endif   
  11.   
  12. /* linux-2.6.38.8/include/linux/kernel.h * 
  13.  * container_of - cast a member of a structure out to the containing structure 
  14.  * @ptr: the pointer to the member. 
  15.  * @type:   the type of the container struct this is embedded in. 
  16.  * @member:    the name of the member within the struct. 
  17.  * 
  18.  */  
  19. #define container_of(ptr, type, member) ({      \   
  20.     const typeof( ((type *)0)->member ) *__mptr = (ptr);    \  
  21.     (type *)( (char *)__mptr - offsetof(type,member) );})  
  22.   
  23. #include <stdio.h>   
  24.   
  25. struct test_struct {  
  26.     int num;  
  27.     char ch;  
  28.     float fl;  
  29. };  
  30.   
  31. int main(void)  
  32. {  
  33.     struct test_struct init_test_struct = { 99, 'C', 59.12 };  
  34.   
  35.     char *char_ptr = &init_test_struct.ch;  
  36.   
  37.     struct test_struct *test_struct = container_of(char_ptr, struct test_struct, ch);  
  38.       
  39.     printf(" test_struct->num = %d\n test_struct->ch = %c\n test_struct->fl = %f\n",   
  40.         test_struct->num, test_struct->ch, test_struct->fl);  
  41.       
  42.     return 0;  
  43. }  

例子輸出結果:

  1. test_struct->num = 99  
  2. test_struct->ch = C  
  3. test_struct->fl = 59.119999  

不適當的例子,如清單3: 

  1. /* linux-2.6.38.8/include/linux/compiler-gcc4.h */  
  2. #define __compiler_offsetof(a,b) __builtin_offsetof(a,b)   
  3.   
  4. /* linux-2.6.38.8/include/linux/stddef.h */  
  5. #undef offsetof   
  6. #ifdef __compiler_offsetof   
  7. #define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)   
  8. #else   
  9. #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)   
  10. #endif   
  11.   
  12. /* linux-2.6.38.8/include/linux/kernel.h * 
  13.  * container_of - cast a member of a structure out to the containing structure 
  14.  * @ptr: the pointer to the member. 
  15.  * @type:   the type of the container struct this is embedded in. 
  16.  * @member:    the name of the member within the struct. 
  17.  * 
  18.  */  
  19. #define container_of(ptr, type, member) ({      \   
  20.     const typeof( ((type *)0)->member ) *__mptr = (ptr);    \  
  21.     (type *)( (char *)__mptr - offsetof(type,member) );})  
  22.   
  23. #include <stdio.h>   
  24.   
  25. struct test_struct {  
  26.     int num;  
  27.     char ch;  
  28.     float fl;  
  29. };  
  30.   
  31. int main(void)  
  32. {  
  33.     char real_ch = 'A';  
  34.     char *char_ptr = &real_ch;  
  35.   
  36.     struct test_struct *test_struct = container_of(char_ptr, struct test_struct, ch);  
  37.   
  38.     printf(" char_ptr = %p  test_struct = %p\n\n", char_ptr, test_struct);  
  39.   
  40.     printf(" test_struct->num = %d\n test_struct->ch = %c\n test_struct->fl = %f\n",   
  41.         test_struct->num, test_struct->ch, test_struct->fl);  
  42.       
  43.     return 0;  
  44. }  

    例子輸出結果: 

  1. char_ptr = 0xbfb72d7f  test_struct = 0xbfb72d7b  
  2.   
  3. test_struct->num = -1511000897  
  4. test_struct->ch = A  
  5. test_struct->fl = 0.000000  

    注意,由於這裡並沒有一個具體的結構體變量,所以成員num和fl的值是不確定的。

Copyright © Linux教程網 All Rights Reserved