Detailed explanation of Linux kernel macro Container_Of

Detailed explanation of Linux kernel macro Container_Of

1. How are structures stored in memory?

int main() 
{ 
 
 Student stu; 
 stu.id = 123456; 
 strcpy(stu.name,"feizhufeifei"); 
 stu.math = 90; 
 stu.PE = 80; 
 printf("Student:%p\r\n",&stu); 
 printf("stu.ID:%p\r\n",&stu.ID); 
 printf("stu.name:%p\r\n",&stu.name); 
 printf("stu.math:%p\r\n",&stu.math); 
 return 0; 
} 

The print results are as follows:

//The address of the structure Student:0xffffcbb0 
//The address of the first member of the structure stu.ID:0xffffcbb0 //Offset address + 0 
stu.name:0xffffcbb4//Offset address + 4 
stu.math:0xffffcbd4 //Offset address + 24 


We can see that the address of the structure is the same as the address of the first member of the structure. This is why we mentioned in Refuse to reinvent the wheel! How to port and use the Linux kernel's general linked list (with complete code implementation) why struct list_head should be placed first in the structure.

If you don’t quite understand, take a look at these two examples:

  • struct A { int a; char b; int c; char d; }; a is at offset 0, b is at offset 4, c is at offset 8 (the smallest integer multiple of 4 greater than 4 + 1), and d is at offset 12. A has alignment 4 and size 16.
  • struct B { int a; char b; char c; long d; }; a is at offset 0, b is at offset 4, c is at offset 5, and d is at offset 8. B has an alignment of 8 and a size of 16.

We can see that the member variables in the structure are actually stored as offset addresses in memory. That is to say, the address of structure A + the offset address of the member variable = the starting address of the structure member variable.

Therefore, we can also infer the address of structure A based on the starting address of the structure variable and the offset address of the member variable.

2. container_of macro

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


??First, let’s look at the three parameters. ptr is the pointer to the member variable, type refers to the type of the structure, and member is the name of the member variable.

The function of the container_of macro is to find the address of a structure variable through the address of a member variable in the structure, the variable name, and the structure type. A trick used here is to use compiler technology, which is to first find the offset of the structure member in the structure, and then find the address of the main structure variable based on the address of the member variable. The following is a detailed analysis of each part.

3. typeof

First, let’s look at typeof, which is used to return the type of a variable. This is an extended function of the GCC compiler, which means that typeof is compiler-dependent. It is neither required by the C language specification nor part of a standard.

int main() 
{ 
 int a = 5; 
 //Here define a variable b of the same type as a 
 typeof(a) b = 6; 
 printf("%d,%d\r\n",a,b);//5 6 
 return 0; 
} 

4. (((type *)0)->member)

((TYPE *)0) converts 0 to a structure pointer of type type. In other words, it makes the compiler think that the structure starts at the beginning of the program segment, 0. If it starts at address 0, the address of the member variable we get is directly equal to the offset address of the member variable.

(((type *)0)->member) refers to the MEMBER member in the structure.

typedef struct student{ 
 int id; 
 char name[30]; 
 int math; 
}Student; 
int main() 
{ 
 //Here, the structure is forced to be converted to address 0, and then the address of name is printed. 
 printf("%d\r\n",&((Student *)0)->name);//4 
 return 0; 
} 

5. const typeof(((type * )0) ->member)*__mptr = (ptr);

This code means to use typeof() to obtain the type of member attribute in the structure, then define a temporary pointer variable __mptr of this type, and assign the address of member pointed to by ptr to __mptr ;

Why not just use ptr instead of doing this? I think it may be to avoid damaging the contents pointed to by ptr and prt .

6. offsetof(type, member))

((size_t) &((TYPE*)0)->MEMBER)

size_t is defined in the standard C library and is generally defined on 32-bit architectures as:

typedef unsigned int size_t;

On 64-bit architectures it is defined as:

typedef unsigned long size_t;

As you can see from the definition, size_t is a non-negative number, so size_t is usually used for counting (because counting does not require a negative area):

for(size_t i=0;i<300;i++)

In order to make the program more portable, the kernel uses size_t instead of int and unsigned。((size_t) &((TYPE*)0)->MEMBER) Combined with the previous explanation, we can know that this sentence means to find the offset value of MEMBER relative to the 0 address.

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

This sentence means converting __mptr to char* 類型. Because the offset obtained by offsetof is in bytes. Subtract the two to get the starting position of the structure, and then force it to be converted to type .

8. Examples

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 
#define container_of(ptr, type, member) ({ \ 
        const typeof( ((type *)0)->member ) *__mptr = (ptr); \ 
        (type *)( (char *)__mptr - offsetof(type,member) );}) 
         
typedef struct student 
{ 
 int id; 
 char name[30]; 
 int math; 
}Student; 
 
int main() 
{ 
    Student stu; 
        Student *sptr = NULL; 
  stu.id = 123456; 
  strcpy(stu.name,"zhongyi"); 
  stu.math = 90; 
        sptr = container_of(&stu.id,Student,id); 
        printf("sptr=%p\n",sptr); 
        sptr = container_of(&stu.name,Student,name); 
        printf("sptr=%p\n",sptr); 
        sptr = container_of(&stu.math,Student,id); 
        printf("sptr=%p\n",sptr); 
        return 0;  
} 


The results are as follows:

sptr=0xffffcb90
sptr=0xffffcb90
sptr=0xffffcbb4

Macro expansion may make it clearer

int main() 
{ 
    Student stu; 
        Student *sptr = NULL; 
  stu.id = 123456; 
  strcpy(stu.name,"zhongyi"); 
  stu.math = 90; 
  //Expand and replace sptr = ({ const unsigned char *__mptr = (&stu.id); (Student *)( (char *)__mptr - ((size_t) &((Student *)0)->id) );}); 
        printf("sptr=%p\n",sptr); 
        //Expand and replace sptr = ({ const unsigned char *__mptr = (&stu.name); (Student *)( (char *)__mptr - ((size_t) &((Student *)0)->name) );}); 
        printf("sptr=%p\n",sptr); 
        //Expand and replace sptr = ({ const unsigned int *__mptr = (&stu.math); (Student *)( (char *)__mptr - ((size_t) &((Student *)0)->math) );}); 
        printf("sptr=%p\n",sptr); 
        return 0;  
} 

This is the end of this article about the detailed explanation of the Container_Of macro in the Linux kernel. For more information about the Container_Of macro in the Linux kernel, please search for previous articles on 123WORDPRESS.COM or continue to browse the following related articles. I hope you will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • C language container of() function case detailed explanation
  • Introduction to container of() function in Linux kernel programming
  • In-depth analysis of the Linux kernel macro container_of
  • Detailed explanation of container_of function in Linux kernel
  • Introduction to C language macro function container of()

<<:  Practical MySQL + PostgreSQL batch insert update insertOrUpdate

>>:  Introduction to the use of this in HTML tags

Recommend

How to set up vscode remote connection to server docker container

Table of contents Pull the image Run the image (g...

A brief discussion on the fun of :focus-within in CSS

I believe some people have seen this picture of c...

Detailed explanation of browser negotiation cache process based on nginx

This article mainly introduces the detailed proce...

js to achieve the effect of dragging the slider

This article shares the specific code of how to d...

How to encapsulate the table component of Vue Element

When encapsulating Vue components, I will still u...

How to use JS to implement waterfall layout of web pages

Table of contents Preface: What is waterfall layo...

Basic tutorial on using explain statement in MySQL

Table of contents 1. Overview 1. Explain statemen...

Vue.js framework implements shopping cart function

This article shares the specific code of Vue.js f...

Limit input type (multiple methods)

1. Only Chinese characters can be input and pasted...

CSS sets the box container (div) height to always be 100%

Preface Sometimes you need to keep the height of ...

WiFi Development | Introduction to WiFi Wireless Technology

Table of contents Introduction to WiFi Wireless T...

Detailed explanation of this pointing problem in JavaScript function

this keyword Which object calls the function, and...

Using docker command does not require sudo

Because the docker daemon needs to bind to the ho...

MySQL index coverage example analysis

This article describes MySQL index coverage with ...