一. 內核中大量使用container_of這個宏,用以進行數據結構的向上轉換,比如這樣一段代碼:
static int
platform_match(struct device * dev, struct device_driver * drv)
{
struct
platform_device *pdev = container_of(dev, struct
platform_device, dev);
return
(strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
}
在這里struct
platform_device定義為:
struct platform_device {
const
char * name;
u32 id;
struct
device dev;
u32 num_resources;
struct
resource * resource;
};
注意,在這個結構體里面包含了
一個叫device的結構體,可以將struct platform_device稱為父結構體,將struct device稱為子結構體。
在上述函數中,傳遞進來的dev指針將指向platform_device這個父結構體中的dev成員,而contianer_of這個宏的作用則在于通過這個結構體成員的指針
獲得其父結構體的指針,在這里就是platform_device這一結構體的指針。
要達到這一目的很容易,只要將
這個成員的指針減去此成員在整個結構體內的偏移量自然就可以了??纯?/span>container_of的定義:
/**
* 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) );})
這個宏定義的第一行構建一個叫__mptr的臨時變量,實際上并不是必須的,它的作用只是強制編譯器對數據類型進行檢查。第二行里面的__mptr實際就是子結構體成員的指針。
偏移量的計算由offsetof宏完成:
#define offsetof(TYPE, MEMBER) ((size_t)
&((TYPE *)0)->MEMBER)
這種方法到處都在用,沒什么新
奇的。
二. 應用舉例:
#define list_entry(ptr, type, member)
\ container_of(ptr, type, member)
#define list_for_each_entry(pos, head, member) \ for (pos
= list_entry((head)->next, typeof(*pos), member);\ prefetch(pos->member.next), &pos->member
!= (head); \ pos =
list_entry(pos->member.next, typeof(*pos), member)) //------------------------------------------------------------- list_entry((head)->next, typeof(*pos), member)返回(head)->next物理指針所處位置向前減去offsetof()個字節(jié)數據之后,
其父變量pos的物理地址,父變量的類型在編譯時由
typeof(*pos)自
動返回(gliethttp). 所以list_for_each_entry遍歷head下面掛接的類型為typeof(*pos)的childs結構
體們,當然每個child結構體包含struct
list_head nodde>
三. container_of(pointer,container_type,container_field);
這個宏需要一個container_field字段的指針,該字段包含在container_type類型的結構中,然后返回包含該字段的結構指針。
*****************************************
但由于是在驅動中實現(xiàn)的這個宏,總不能自己編寫一個驅動來測試這個宏吧(目前的水平而言,編寫一個驅動還是比較費勁哦),有沒有辦法在用戶空間測試呢?有
的。我們可以將linux/kernel.h頭文件包含進來,直接在用戶空間測試這個宏的巧妙之處。下面就是我的測試代碼。
main.c文件
#include <linux/unistd.h>
#include <linux/string.h>
#include <linux/stdlib.h>
#include <linux/kernel.h>
struct cona_t{
int i;
int j;
int v;
char t[10];
unsigned short xy;
};
struct cona_t ct;
unsigned short xy;
int main(int argc,char * argv[])
{
int xy;
struct cona_t * p;
memset(&ct,0,sizeof(struct cona_t));
ct.i = ct.j = ct.v = 10;
sprintf(ct.t,"%s","sdf");
ct.xy = 20;
p = container_of(&ct.xy,struct cona_t,xy);
printf("%s\n",p->t);
return 0;
}
下面是Makefile文件
CC=gcc
TAR=main
SRC=main.c
KERNEL_INCLUDE ?= /lib/modules/$(shell uname -r)/build/include
all:
$(CC) -D__KERNEL__ -o $(TAR) -I$(KERNEL_INCLUDE) $(SRC)
clean:
rm -f $(TAR)
看看一下結果吧。是不是正好得到了我們想要的東西?
如果打開kernel.h文件,就會發(fā)現(xiàn)這個container_of這個宏定義如下:
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
offsetof又被如下定義:
#ifdef __KERNEL__
#undef offsetof
#ifdef __compiler_offsetof
#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
那個__com
|