了解 Linux 内核中的 Container_of 宏

当我浏览 Linux 内核时,我发现了一个 container_of宏,它的定义如下:

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

我知道 Container _ of 是做什么的,但是我不明白的是最后一句话,也就是

(type *)( (char *)__mptr - offsetof(type,member) );})

如果我们按以下方式使用宏:

container_of(dev, struct wifi_device, dev);

最后一句的相应部分是:

(struct wifi_device *)( (char *)__mptr - offset(struct wifi_device, dev);

看起来什么都没做。 有人能填补这里的空缺吗?

107265 次浏览

The last sentence cast:

(type *)(...)

a pointer to a given type. The pointer is calculated as offset from a given pointer dev:

( (char *)__mptr - offsetof(type,member) )

When you use the cointainer_of macro, you want to retrieve the structure that contains the pointer of a given field. For example:

struct numbers {
int one;
int two;
int three;
} n;


int *ptr = &n.two;
struct numbers *n_ptr;
n_ptr = container_of(ptr, struct numbers, two);

You have a pointer that points in the middle of a structure (and you know that is a pointer to the filed two [the field name in the structure]), but you want to retrieve the entire structure (numbers). So, you calculate the offset of the filed two in the structure:

offsetof(type,member)

and subtract this offset from the given pointer. The result is the pointer to the start of the structure. Finally, you cast this pointer to the structure type to have a valid variable.

Your usage example container_of(dev, struct wifi_device, dev); might be a bit misleading as you are mixing two namespaces there.

While the first dev in your example refers to the name of pointer the second dev refers to the name of a structure member.

Most probably this mix up is provoking all that headache. In fact the member parameter in your quote refers to the name given to that member in the container structure.

Taking this container for example:

struct container {
int some_other_data;
int this_data;
}

And a pointer int *my_ptr to the this_data member you'd use the macro to get a pointer to struct container *my_container by using:

struct container *my_container;
my_container = container_of(my_ptr, struct container, this_data);

Taking the offset of this_data to the beginning of the struct into account is essential to getting the correct pointer location.

Effectively you just have to subtract the offset of the member this_data from your pointer my_ptr to get the correct location.

That's exactly what the last line of the macro does.

It is an utilisation of a gcc extension, the statements expressions. If you see the macro as something returning a value, then the last line would be :

return (struct wifi_device *)( (char *)__mptr - offset(struct wifi_device, dev);

See the linked page for an explanation of compound statements. Here is an example :

int main(int argc, char**argv)
{
int b;
b = 5;
b = ({int a;
a = b*b;
a;});
printf("b %d\n", b);
}

The output is

b 25

A little real context says clearer, below use red-black tree as example, which is the way that I understand container_of.

as Documentation/rbtree.txt states, in linux kernel code, it's not rb_node contain data entry, rather

Data nodes in an rbtree tree are structures containing a struct rb_node member.

struct vm_area_struct (in file include/linux/mm_types.h:284) is such a structure,

in the same file, there is a macro rb_entry which is defined as

#define rb_entry(ptr, type, member) container_of(ptr, type, member)

clearly, rb_entry is same as container_of.

at mm/mmap.c:299 inside function definition browse_rb, there is a usage of rb_entry:

static int browse_rb(struct mm_struct *mm)
{
/* two line code not matter */
struct rb_node *nd, *pn = NULL; /*nd, first arg, i.e. ptr. */
unsigned long prev = 0, pend = 0;


for (nd = rb_first(root); nd; nd = rb_next(nd)) {
struct vm_area_struct *vma;
vma = rb_entry(nd, struct vm_area_struct, vm_rb);
/* -- usage of rb_entry (equivalent to container_of) */
/* more code not matter here */

now it is clear, in container_of(ptr, type, member),

  • type is the container struct, here struct vm_area_struct
  • member is name of a member of type instance, here vm_rb, which is of type rb_node,
  • ptr is a pointer pointing member of an type instance, here rb_node *nd.

what container_of do is, as in this example,

  • given address of obj.member (here obj.vm_rb), return the address of obj.
  • since a struct is a block of contiguous memory, address of obj.vm_rb minus offset between the struct and member will be the container's address.

include/linux/kernel.h:858 -- definition of container_of

include/linux/rbtree.h:51 -- definition of rb_entry

mm/mmap.c:299 -- usage of rb_entry

include/linux/mm_types.h:284 -- struct vm_area_struct

Documentation/rbtree.txt: -- Documentation of red-black tree

include/linux/rbtree.h:36 -- definition of struct rb_node

P.S.

Above files are in current develop version, i.e, 4.13.0-rc7.

file:k mean kth line in file.

conatainer_of() macro in Linux Kernel -

When it comes to managing several data structures in code, you'll almost always need to embed one structure into another and retrieve them at any moment without being asked questions about memory offsets or boundaries. Let's say you have a struct person, as defined here:

 struct person {
int age;
int salary;
char *name;
} p;

By only having a pointer on age or salary, you can retrieve the whole structure wrapping (containing) that pointer. As the name says, the container_of macro is used to find the container of the given field of a structure. The macro is defined in include/linux/kernel.h and looks like the following:

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

Don't be afraid of the pointers; just see them as follows:

container_of(pointer, container_type, container_field);

Here are the elements of the preceding code fragment:

  • pointer: This is the pointer to the field in the structure
  • container_type: This is the type of structure wrapping (containing) the pointer
  • container_field: This is the name of the field to which pointer points inside the structure

Let's consider the following container:

struct person {
int age;
int salary;
char *name;
};

Now, let's consider one of its instances, along with a pointer to the age member:

struct person somebody;
[...]
int *age_ptr = &somebody.age;

Along with a pointer to the name member (age_ptr),you can use the container_of macro in order to get a pointer to the whole structure (container) that wraps this member by using the following:

struct person *the_person;
the_person = container_of(age_ptr, struct person, age);

container_of takes the offset of age at the beginning of the struct into account to get the correct pointer location. If you subtract the offset of the field age from the pointer age_ptr, you will get the correct location. This is what the macro's last line does:

(type *)( (char *)__mptr - offsetof(type,member) );

Applying this to a real example, gives the following:

struct family {
struct person *father;
struct person *mother;
int number_of_sons;
int family_id;
} f;


/*
* Fill and initialise f somewhere   */      [...]


/*
* pointer to a field of the structure
* (could be any (non-pointer) member in the structure)
*/
int *fam_id_ptr = &f.family_id;
struct family *fam_ptr;


/* now let us retrieve back its family */
fam_ptr = container_of(fam_id_ptr, struct family, family_id);

The container_of macro is mainly used in generic containers in the kernel.

That's all about container_of macro in kernel.

Very useful link for understanding container_of macro in linux kernel. https://linux-concepts.blogspot.com/2018/01/understanding-containerof-macro-in.html

Most Simplest Implementation of Container _of macro is below , It reduces all complex checking of type and works

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ((type *)((char *)(ptr) - offsetof(type, member)))

ptr will give address of member and just subtract offset difference and you will get start address.

Example usage

struct sample {
int mem1;
char mem2;
int mem3;
};
int main(void)
{


struct sample sample1;


printf("Address of Structure sample1 (Normal Method) = %p\n", &sample1);
printf("Address of Structure sample1 (container_of Method) = %p\n",
container_of(&sample1.mem3, struct sample, mem3));


return 0;
}