分享

container_of这个宏

 lchjczw 2012-12-14

一. 内核中大量使用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这一结构体的指针。

要达到这一目的很容易,只要将 这个成员的指针减去此成员在整个结构体内的偏移量自然就可以了。看看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()个字节数据之后, 其父变量pos的物理地址,父变量的类型在编译时由 typeof(*pos)自 动返回(gliethttp).
所以list_for_each_entry遍历head下面挂接的类型为typeof(*pos)的childs结构 体们,当然每个child结构体包含struct list_head nod
de>




三.  container_of(pointer,container_type,container_field);
这个宏需要一个container_field字段的指针,该字段包含在container_type类型的结构中,然后返回包含该字段的结构指针。
*****************************************
但由于是在驱动中实现的这个宏,总不能自己编写一个驱动来测试这个宏吧(目前的水平而言,编写一个驱动还是比较费劲哦),有没有办法在用户空间测试呢?有 的。我们可以将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文件,就会发现这个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

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多