The Linux kernel contains a very useful macro named “container_of
”. This article is about Understanding the container_of macro in Linux kernel. This article includes a simple program that illustrates how this macro is used and explains why it is so useful.
You can also read flexible array members in c, macro vs inline, and embedded interview topics.
Understanding the container_of macro in Linux kernel
Introduction
Now we will assume that we have three containers. Container-1 has gold, Container-2 has diamond and Container-3 has filled with Water (Probably water is the most precious than gold and diamond). One guy comes and asks you, which container contains Water (Container of water). I need it desperately. You would say, Container-3 has water.
So in this example, that guy knows that one container contains water, but doesn’t know which container it is. He was using the member name (water) to know the container name or container address. So this is the use of Container_of macro in the Linux kernel.
I know the address of one member in the structure. But I don’t know the address of that structure. That structure may have many members. So we can find the address of the structure using this container_of
macro in Linux Kernel.
Hope you understood this concept. If you don’t, you can understand that by using examples.
container_of()
container_of()
is a macro defined in <linux/kernel.h>
The syntax of the Macro is given below.
Where,
It returns the address of the container structure of the member. |
We will see the example. This Example is not the Linux kernel example. This is just a C example. I’ll show you the Linux kernel example later in this post.
Example
Container_of macro will not be there in the normal C program library. This is the Linux Kernel library. So manually I’ve added the macro to my program. Container_of macro is used to find the container structure address of the given member. In this example, I’ve declared one structure that contains two members (mem1
, mem2
). I’m going to get the structure address by using the member mem2
.
#include <stdio.h> #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) );}) int main(void) { struct sample { int mem1; char mem2; }; 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.mem2, struct sample, mem2)); return 0; }
Since we know the address of the structure sample1
already. But, for your understanding about the container_of
I made a very easy program. Okay, What will be the output? Both addresses should be the same or different? Let’s see the output.
Output
Address of Structure sample1 (Normal Method) = 0x7ffd0d058780 Address of Structure sample1 (container_of Method) = 0x7ffd0d058780
Yessss!!!!!. Both addresses should be the same. I hope you understood the concept of container_of macro. Do you know how that container of macro works? Start to read more.
Working of the Container_of macro
The full expansion of this macro is shown below.
/** * 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) );})
Did you understand anything by seeing this macro? Ok, we will go step by step. Now we will take the first line.
const typeof( ((type *)0)->member ) *__mptr = (ptr);
Here everyone knows the use of const. So I’ll explain typeof
in this line.
typeof()
Since typeof
is a compiler extension, there is not really a definition for it, but in the tradition of C it would be an operator like sizeof
. It takes one argument and returns the static type of the operand. Just see the below example.
Example
#include <stdio.h> int main(void) { char name[20] = "EmbeTronicX"; typeof(name) role = "Tutorial"; printf("%s %s\n", name, role); printf("Size of name = %d, Size of role = %d\n",sizeof(name),sizeof(role)); return 0; }
Output
Here is the output.
EmbeTronicX Tutorial Size of name = 20, Size of role = 20
If you see the output carefully, the type of the name
is assigned to the role
.
Zero Point Reference
But what about the zero-pointer dereference? Well, it’s a little pointer magic to get the type of member. It won’t crash, because the expression itself will never be evaluated. All the compiler cares for is its type. The same situation occurs in case we ask back for the address. The compiler again doesn’t care for the value, it will simply add the offset of the member to the address of the structure, in this particular case 0, and return the new address.
Example
#include <stdio.h> int main(void) { struct sample { int mem1; char mem2; }; printf("Offset of the member = %d\n", &((struct s*)0)->mem2); return 0; }
Output
Offset of the member = 4
In this example, it returns the offset of the mem2
. mem1
will occupy offset 0 to 3. So mem2
will take 4.
So, Also note that the following two definitions are equivalent:
|
|
Now let’s take the original example that we have seen earlier in this post.
const typeof( ((type *)0)->member ) *__mptr = (ptr)
this line creates the local constant pointer variable.
const typeof( ((type *)0)->member ) *__mptr = (ptr)
—–> const char * __mptr = &sample1.mem2
Now we can take the second line which is (type *)( (char *)__mptr - offsetof(type,member) ).
offsetof()
offsetof
is a macro that will return a byte offset of a member to the beginning of the structure. The macro is shown below.
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
It is even part of the standard library (available in stddef.h
).
We have already seen the example for this &((TYPE *)0)->MEMBER)
.
It returns an address of a member called MEMBER of a structure of type TYPE that is stored in memory from address 0 (which happens to be the offset we’re looking for).
Now we need to convert this both macro (offsetof()
, container_of()) based on our original example. These macros will replace our example like below.
container_of(&sample1.mem2, struct sample, mem2) || || \/ const char * __mptr = &sample1.mem2; struct sample * ((char*) __mptr - &((struct sample*)0)->mem2) || || \/ const char* __mptr = 0x7FFD0D058784; //(Address of mem2 is 0x7FFD0D058784) struct sample * (0x7FFD0D058784 - 4) || || \/ struct sample* (0x7FFD0D058780) //This is the address of the container structure
Linux Kernel Driver Example Scenario
This is the macro of the Linux Kernel. Below snippet is the example scenario of container_of
used in Linux Kernel driver.
struct foo { spinlock_t lock; struct workqueue_struct *wq; struct work_struct offload; (...) }; static void foo_work(struct work_struct *work) { struct foo *foo = container_of(work, struct foo, offload); (...) } static irqreturn_t foo_handler(int irq, void *arg) { struct foo *foo = arg; queue_work(foo->wq, &foo->offload); (...) } static int foo_probe(...) { struct foo *foo; foo->wq = create_singlethread_workqueue("foo-wq"); INIT_WORK(&foo->offload, foo_work); (...) }
You can also read the below tutorials.
Embedded Software | Firmware | Linux Devic Deriver | RTOS
Hi, I’m SLR. I am a tech blogger and an Embedded Engineer. I am always eager to learn and explore tech-related concepts. And also, I wanted to share my knowledge with everyone in a more straightforward way with easy practical examples. I strongly believe that learning by doing is more powerful than just learning by reading. I love to do experiments. If you want to help or support me on my journey, consider sharing my articles, or Buy me a Coffee! Thank you for reading my blog! Happy learning!