In the process of learning Linux drivers, a macro called container_of is encountered.
The macro is defined in Include/linux/kernel.h, first to post its code:
/**
* 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 was 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));})
Its role is obvious, and that isGets a pointer to the entire struct variable, based on a pointer to a domain member variable in a struct variable。 For example, there is a struct variable, which is defined as follows:
struct Demo_struct {
Type1 Member1;
Type2 Member2;
Type3 Member3;
Type4 Member4;
};
struct DEMO_STRUCT demo;
At the same time, in another place, a pointer to a field member variable in the variable demo is obtained, such as:
Type3 *memp = Get_member_pointer_from_somewhere ();
At this point, if you need to get a pointer to the entire struct variable, not just a pointer to one of its domain member variables, we can do this:
struct Demo_struct *demop = container_of (memp, struct demo_struct, member3);
In this way, we get a pointer to the entire struct variable through a pointer to a domain member variable of a struct variable.
Let me say my understanding of the realization of this container_of:
First, we will expand the container_of (memp, struct demo_struct, type3) as defined by the macro as follows:
struct Demo_struct *demop = ({\
Const typeof ((struct demo_struct *) 0)->member3) *__mptr = (memp); \
(Struct demo_struct *) ((char *) __mptr-offsetof (struct demo_struct, Member3));})
Among them, typeof is the extension of GNU C to standard C, and its function is to get the type of variable based on the variable. Therefore, the function of line 2nd in the preceding code is to first use typeof to get the struct domain variable member3 the type is type3, then define a temporary variable __mptr for the Type3 pointer type, and assign the value of the pointer memp of the field variable in the actual struct variable to the temporary variable __ Mptr.
(char *) __mptr converted to a byte-type pointer. (char *) __mptr-offsetof (Type,member)) is used to find the struct start address (char * type pointer), then (type *) ((char *) __mptr-offsetof (Type,member)) in (Typ E *) to convert a byte-type struct start pointer to a type * type of struct start pointer.
Assume that the structure variable demo is in the actual memory position as shown in:
Demo
+-------------+ 0xa000
| Member1 |
+-------------+ 0xa004
| Member2 |
+-------------+ 0xa010
| Member3 |
+-------------+ 0xa018
| Member4 |
+-------------+
Then, after the 2nd line of the above code is executed, the value of __mptr is 0xa010.
Look at line 3rd of the above code, which needs to be described as offsetof, which is defined in include/linux/stddef.h, which is defined as follows:
#define OFFSETOF (Type, MEMBER) ((size_t) & ((TYPE *) 0)->member)
First analyze the operation mechanism of this macro:
Altogether 4 steps
1. ((TYPE *) 0) transform 0 to type pointer;
2. ((TYPE *) 0)->member access to data members in the structure;
3. & (((TYPE *) 0)->member) take out the address of the data member;
4. (size_t) (& ((type*) 0)->member) result conversion type. The clever thing is to convert 0 to (type*), the structure of the memory space first address 0 as the starting address, the member address is naturally offset address;
Again, we will expand the offsetof call above, that is:
(Struct demo_struct *) ((char *) __mptr-((size_t) & ((struct demo_struct *) 0)->member3));
As can be seen, the implementation principle of offsetof is to take the offset address of the domain member in the struct relative to address 0, that is, the offset of the domain member variable with respect to the first address of the struct variable.
Therefore, the value returned by the Offsetof (struct demo_struct, Member3) call is the offset of the Member3 relative to the demo variable. The Offsetof (struct demo_struct, Member3) will return 0x10, combined with the variable address distribution shown above.
Thus, from the above analysis, this time, __mptr==0xa010,offsetof (struct demo_struct, Member3) ==0x10.
Therefore, (char *) __mptr-((size_t) & ((struct demo_struct *) 0)->member3) = = 0xa010-0x10 = 0xa000, which is the first address of the structure variable demo (as shown in )。
This is the first pointer to the struct body from a member variable pointer. A pointer type is converted from a struct member variable type to that struct type.
As a result, container_of implements a pointer to a domain member variable in a struct variable to obtain a pointer to the entire struct variable.
The above content is from the network, this article analysis is very thorough, by the way, Song Baohua "Linux device Driver Development detailed" P132 the last line of the macro parameter interpretation is wrong! Of course, do not cover the leisure of yoga!
Here are some of my own understandings:
First, I defined a character device structure body
struct GLOBALMEM_DEV
{
struct Cdev My_cdev; The underlying structure of a character device
unsigned char mem[globalmem_size];
struct Semaphore sem;/
};
Next I instantiate a pointer object for the device
struct Globalmem_dev *pdev;
And then I used it in the open function.
int Globalmem_open (struct inode *inode, struct file *filp) about the generation and extinction of FILP see "Driver Detail" P92
{
struct Globalmem_dev *pdev;
PRINTK ("\nfunction globalmem_open invoked\n");
Pdev = container_of (Inode->i_cdev, struct globalmem_dev, My_cdev);
Filp->private_da
Ta = Pdev;
if (Down_trylock (&PDEV->SEM))//Get the semaphore, really I love container_of!!!! I love to die container_of!!!
Return-ebusy;
return 0;
}
A description of the above usage:
Parameter 3 is the name of a member of parameter 2 of this struct! Instead of the type name! Parameter 1 is a pointer to the member of parameter 3
The I_cdev field in the Inode is a pointer, and when we successfully insmod a device driver, we create a device file node through Mknod and want to associate it with a specific device (driver), which corresponds to an instance of the struct inode structure. , this struct has a field i_cdev, a pointer to a struct CDEV type, which points to the My_cdev field of the device structure body. At this point you have a pointer to the My_cdev field of a Globalmem_dev (the memory allocation assumption is complete before calling open Pdev) so container_of can help you figure out pointers to the device structure.
When a device drives multiple devices (sub-devices), you know what the container_of play! When you call open for each device, because each device corresponding to the device file node is different, then according to the node's I_cdev field calculation of the device structure pointer is not the same, you can find a specific node corresponding to the device structure! Rather than writing the same device drivers for different sub-devices.
Transferred from: http://dev.firnow.com/course/6_system/linux/linuxjq/20100313/198611.html
"Go" container_of macro analysis