container_of

2021-01-10  本文已影响0人  _invincible_

功能

通过结构体成员变量的地址获取其结构体变量(container)的地址。

定义

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

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

例子

#include <stdlib.h>
#include <stdio.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) );})

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


struct node_t {
  struct node_t *next;
};

struct my_struct {
   int a;
   char b;
   struct node_t c;
};


int main(){
    struct my_struct m;
    struct my_struct m2;
    struct my_struct *pm2;
    
    int * pa = &m.a;
    char * pb = &m.b;
    struct node_t * pc = &m.c;
    
    void *ptr;
    
    ptr = container_of(pc, struct my_struct, c);
    
    printf("&m: %p, ptr: %p\n", &m, ptr);
    ptr = NULL;
    
    ptr = container_of(pa, struct my_struct, a);
    printf("&m: %p, ptr: %p\n", &m, ptr);
    ptr = NULL;
        
    ptr = container_of(pb, struct my_struct, b);
    printf("&m: %p, ptr: %p\n", &m, ptr);
    
    m.c.next = &m2.c;
    pm2 = container_of(m.c.next, struct my_struct, c);
    printf("&m2: %p, pm2: %p\n", &m2, pm2);

    return 0;
}



&m: 0x7ffe4c4393d0, ptr: 0x7ffe4c4393d0
&m: 0x7ffe4c4393d0, ptr: 0x7ffe4c4393d0
&m: 0x7ffe4c4393d0, ptr: 0x7ffe4c4393d0
&m2: 0x7ffe4c4393c0, pm2: 0x7ffe4c4393c0

简单解释

1.计算结构体成员相对 container 的偏移
2.用当前成员的地址减去偏移得到container结构体的地址

第一步的实现

(size_t) &((TYPE *)0)->MEMBER
#include <stdlib.h>
#include <stdio.h>

struct node_t {
  struct node_t *next;
};

struct my_struct {
   int a;
   char b;
   struct node_t c;
};


int main(){
    struct my_struct m;
    printf("&m: %p\n", &m);
    printf("&m.a: %p\n", &m.a);
    printf("&m.b: %p\n", &m.b);
    printf("&m.c: %p\n", &m.c);
    printf("%lu\n", (size_t) & ((struct my_struct *)0)->a);
    printf("%lu\n", (size_t) & ((struct my_struct *)0)->b);
    printf("%lu\n", (size_t) & ((struct my_struct *)0)->c);
    return 0;
}


&m: 0x7ffd2af67fd0
&m.a: 0x7ffd2af67fd0
&m.b: 0x7ffd2af67fd4
&m.c: 0x7ffd2af67fd8
0
4
8

第二步的实现

const typeof( ((type *)0)->member ) *__mptr = (ptr);    
(type *)( (char *)__mptr - offsetof(type,member) )
#include <stdlib.h>
#include <stdio.h>

struct node_t {
  struct node_t *next;
};

struct my_struct {
   int a;
   char b;
   struct node_t c;
};


int main(){
    struct my_struct m;
    struct my_struct *pm;

    size_t offsetof_c = (size_t) & ((struct my_struct *)0)->c;
    printf("offsetof_c: %lu\n", offsetof_c);

    typeof(((struct my_struct *)0)-> c) *__mptr = &m.c;
    printf("__mptr: %p\n", __mptr);
    
    pm = (struct my_struct *)( (char *)__mptr - offsetof_c);
    printf("&m: %p, pm: %p\n", &m, pm);

    return 0;
}


offsetof_c: 8
__mptr: 0x7ffcc562a9c8
&m: 0x7ffcc562a9c0, pm: 0x7ffcc562a9c0

常见应用

// include/linux/rbtree.h
#define rb_entry(ptr, type, member) container_of(ptr, type, member)

// include/linux/vmalloc.h
struct vmap_area {
        unsigned long va_start;
        unsigned long va_end;
        unsigned long flags;
        struct rb_node rb_node;         /* address sorted rbtree */
        // ...
};

// mm/vmalloc.c
static struct vmap_area *__find_vmap_area(unsigned long addr)
{
        struct rb_node *n = vmap_area_root.rb_node;

        while (n) {
                struct vmap_area *va;
...
                va = rb_entry(n, struct vmap_area, rb_node);
...
        }
...
}

// include/linux/list.h
#define list_entry(ptr, type, member) \
        container_of(ptr, type, member)

// include/linux/types.h
struct list_head {
        struct list_head *next, *prev;
};


// include/linux/vmalloc.h
struct vmap_area {
...
        struct list_head list;          /* address sorted list */
...
};

// mm/vmalloc.c
static void *s_next(struct seq_file *m, void *p, loff_t *pos)
{
        struct vmap_area *va = p, *next;

        ++*pos;
        next = list_entry(va->list.next, typeof(*va), list);
...
        return NULL;
}


上一篇 下一篇

猜你喜欢

热点阅读