11.Linux驱动基础-container_of

219 阅读2分钟

@[toc]

前言

在Linux内核中经常会见到container_of这个宏,通过某一个成员变量的地址获取这个结构体的地址,比如在一个工作队列中获取一个结构体的地址,watchdog_workplat_data的一个成员变量

static void work_func(struct work_struct *work)
{
    struct plat_data *data = container_of(work, struct plat_data, watchdog_work);
}

结构体变量在结构体的地址偏移

从结构体首地址开始,每一个结构体变量对于结构体的首地址有一个固定的地址偏移,当结构体的首地址为0时,此时结构体中各个成员变量的地址就是相对于结构体首地址的偏移 代码示例:

#include <stdio.h>
#include <stdlib.h>


struct s{
   int age;
   int id;
   char *name;
};

int main()
{ 
	//地址
    printf("&s =%p\n",(struct s*)0);
    printf("&age =%p\n",&((struct s*)0)->age);
    printf("&id  =%p\n",&((struct s*)0)->id);
    printf("&name=%p\n",&((struct s*)0)->name);

    return 0;
}

结果:

&s =(nil)
&age =(nil)
&id  =0x4
&name=0x80

container_of宏应用

在Linux内核中定义了offset用来实现这个功能,定义为

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

/**
 * 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) );})

其中offsetof宏定义则是计算MEMBER成员变量相对于结构体变量的地址偏移值,container_of三个参数为:

  • ptr:结构体内成员member的地址
  • type:结构体类型
  • member:结构体变量 程序示例:
#include <stdio.h>


#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

/**
 * 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) );})


struct s
{
    int id;
    char sex;
    int age;
};

int main(void)
{
    struct s s1 = {4,'S',6};
	
    printf("&s1 = %p\n", &s1);
	printf("&s1.sex = %p\n", &s1.sex);
	printf("offsetof(struct s,sex)= %ld\n",offsetof(struct s,sex));
    printf("&s1 = %p\n", container_of(&s1.sex, struct s, sex));
	printf("&s1 = %p\n", (struct s*)((char *)&s1.sex-offsetof(struct s,sex)));
	printf("s1->id=%d\n",(struct s*)((char *)&s1.sex-offsetof(struct s,sex))->id);
    return 0;
}

结果为:


&s1 = 0x7ffe5da751ec
&s1.sex = 0x7ffe5da751f0
offsetof(struct s,sex)= 4
&s1 = 0x7ffe5da751ec
&s1 = 0x7ffe5da751ec
s1->id=4

将上面示例展开为:

#define container_of(ptr, type, member) 
({        
    const typeof( ((type *)0)->member ) *__mptr = (ptr);   
    (type *)( (char *)__mptr - offsetof(type,member) );
})
    
container_of(&s1.sex, struct s, sex)
const typeof( ((struct s*)0)->sex) *__mptr = (&s1.sex); //
 (struct s *)( (char *)&s1.sex - offsetof(type,member)

上面的mptr为s1.sex的地址,(struct s *)( (char *)&s1.sex - offsetof(type,member)的值为s1的地址值,(struct s*)((char *)&s1.sex-offsetof(struct s,sex))->id的值为4,我们在Linux内核编程中,传给某个函数的参数是某个结构体的成员变量,在这个函数中可能还会用到此结构体的其它成员变量,这个时候通过宏变量container_of ,找到结构体的首地址,然后就可以访问其它成员变量了。