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

Example code for implementing background transparency and opaque text with CSS3

Recently, I encountered a requirement to display ...

Web form creation skills

In fact, the three tables above all have three ro...

Detailed explanation of viewing and setting file permissions on Mac

Preface To modify file permissions in the termina...

Summary of common MySQL table design errors

Table of contents Mistake 1: Too many columns of ...

How to use Volume to transfer files between host and Docker container

I have previously written an article about file t...

Teach you how to implement Vue3 Reactivity

Table of contents Preface start A little thought ...

Implementation of Linux command line wildcards and escape characters

If we want to perform batch operations on a type ...

Front-end state management (Part 2)

Table of contents 1. Redux 1.1. Store (librarian)...

Implementation of the Pycharm installation tutorial on Ubuntu 18.04

Method 1: Download Pycharm and install Download a...

MySQL5.7.27-winx64 version win10 download and installation tutorial diagram

MySQL 5.7 installation We are learning MySQL data...

Simple summary of tomcat performance optimization methods

Tomcat itself optimization Tomcat Memory Optimiza...

The difference between key and index in MySQL

Let's look at the code first: ALTER TABLE rep...