The first macro kernel

The first macro kernel

LIST_ENTRY () has a first core macro name, which is designed to return a pointer to a pointer structure by structure member. Let us step by step through the analysis to uncover the mystery of it, feel the subtleties of the kernel of the first macro design.

The idea of ​​finishing analysis

LIST_ENTRY () is defined in the kernel list.h source / include / linux directory, as follows:

#define list_entry(ptr, type, member) \
                container_of(ptr, type, member)

In the definition list _entry, we see the emergence of another macro container _of. And it is through this macro list _entry container _of to achieve. Therefore, we must first enter the container _of, take a look at what it does.

container_of defined in /include/linux/kernel.h, defined as follows:

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

We found that in the definition of container_of, but also the emergence of a new macro offsetof. So, before you start analyzing container _of, it is necessary to make it clear offsetof.

offsetof defined in /include/linux/stddef.h, defined as follows:

#define offsetof(TYPE, MEMBER)  ((size_t) &((TYPE *)0)->MEMBER)

We can see that in the definition offsetof, it has been no longer introduce new macros, so we have to offsetof as a breakthrough for analysis.

Formal analysis

Macro offsetof

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

Word offset means offsets, so we can look at the name suggests, the role of macro offsetof possible and offset related. Well, who is it requires offset it?

  • offsetof TYPE structure for calculating an offset MEMBER members.

Offsetof can be seen from the definition of at & ((TYPE *) 0) -> MEMBER, there is a clear casts ((TYPE *) 0). In the C language, the cast has two syntax:

1.(TYPE)var_name; //变量名形式,如(int)i;
2.(TYPE)varlue;   //值形式,如(type*)0;

Used in the definitions of the second syntax, the value 0 cast into a pointer TYPE structure. By later this casts address TYPE structure becomes 0, then why do such a conversion? What is its role?
In fact, it is doing so only one purpose, that is to make it easier to get members of the offset. We know the type of structure in the pre-compile time, in order to enable the CPU to quickly access data and saving storage space, there is a memory alignment problem is that each member of the structure to be stored in memory in accordance with certain store offsets. Therefore, due to the different types of members, resulting in an offset of each member are not the same, so we will not be able to once and for all to all members set a fixed offset value. Then we want to get the offset of a member of the how to do it? We put this task to the compiler. We can direct the compiler, it "hand over" offset members. One thing we must be clear, in the pre-compiler compile time, the offset of each member is well aware of that, so the compiler if you want to know the address of a member, it only requires a structure of address + offset member can get the address of the member.
Here Insert Picture Description

As a simple example: In the above chart, for example, if the address structure of the above p = 1000 C ,, members offset (offset) is 4, and that the address of the member C is pc = 1004 1000 + 4;

This time to get the address of 1004 is a member of C pc, but we want is not its address, but it's offset, this time how to do it? The easiest way is to directly address the structure becomes zero can not it do? 0 plus a number equal to the number itself, this is just the result of adding the offset members. That is why to be defined by the address of the cast structure becomes 0, for example:

Now the structure of the address p = 0, C members offset (offset) + 4 = 4,0 or 4, the result is exactly the offset of the member.

So we let the compiler to perform & ((TYPE *) 0) -> MEMBER this sentence, it is to do such a thing, it first address type type structure becomes 0, then go to add members mEMBER offset, 0 + offset = offset, the final result is the offset of the member. Kernel designers, it is through this clever design, to direct the compiler to produce offset.

So, when we call offsetof (TYPE, MEMBER), will give members MEMBER TYPE offset in the structure of the

Here it is worth thinking about is: & ((the TYPE *) 0) -> MEMBER, the address of the structure into a 0 through mandatory conversion,
we know the address 0 is reserved for the operating system to use, and there's content is not allowed to access the common procedure. But here puts into a structure address 0, 0 that directly address will not cause the program to crash?

The answer is that the program does not crash, the compiler execution & ((TYPE *) 0) -> MEMBER of the time, does not really address to access the contents of 0, but only as an addition to the value 0 in operation addend to deal with. The image of that is the compiler just take off your house number used to make the calculation of the room, open the door and did not pick up anything in the house. Leave it in the finish after the addition, the house is something that is intact. The reason that the compiler does not take things into the house, because the "&" exists, the compiler sees the "&", you will understand that I just need to get the address on it. Here a simple example to illustrate:

Here Insert Picture Description

Print results are as follows:

Here Insert Picture Description

根据打印结果可以看到:pst->j与&(pst->j)效果是不一样的
pst->j		//没有“&”,会访问变量中的内容,打印结果为成员变量中的内容
&(pst->j)	//有“&”,不会访问变量中的内容,只拿地址,打印结果为成员的地址

At this point, the role of offsetof we already know. In the definition of container_of used offsetof, that is to say, in the realization of container _of in, it will need to get offsetof offset a member of the structure, then what is the role of container _of? It is to offset what use is it? Next, let us enter the world container _of it.

Macro container_of

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

After entering the container _of the world, we find that there are two "familiar strangers" are typeof and "({})." The two small partners, we are in the C language they did not see, it is only because they "live" in the GNU C compiler. To make it easier for us in understanding container _of journey, we need to spend some time and typeof and "({})" These two outstanding junior partner to make friends, to learn what they are.

typeof
  • typeof is the GNU C compiler-specific keywords
  • typeof only take effect at compile time, get used to the type of a variable

for example:

int i = 100;
typeof(i) j = i; <=> int j = i;  //这两个语句的作用是等价的,变量i的类型是int,typeof(i)就相当于拿到变量i的类型
({ })
  • ({}) Syntax extension is GNU C compiler
  • ({}) Similar comma expression, the result is the value of the last statement

for example:

Here Insert Picture Description

Well, we have recognized the typeof and "({})" two small partners, container _of this understanding will be of great help to us. Now, we can officially analysis container _of macros. Let us once again to the definition of container _of moved here:

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

Used in the definition of the extended syntax "({})" has already been said, it is the result of the last statement of value, so, we can directly look at the last statement.

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

And there are a pointer __mptr, which is defined in the second row, the type obtained by the typeof. And the pointer value of the pointer ptr __mptr is the same, and is a parameter ptr container _of macro, which is a pointer type structural body member member, it also points __mptr type structural body member member. In order to clearly express this relationship, we use a diagram to represent their relationship in the following figure:

We look at (char ) __ mptr - offsetof (of the type, Member) what is the meaning of this sentence. Can be obtained by offsetof (type, member) is offset member member, the image above is offset, then subtracting __mptr offset, to obtain an address, P as shown above, and the address is the address structure , thus achieving a start address of the structure found by members. __mptr char foregoing is for the purpose of pointer arithmetic to effect byte-wise subtraction. Finally (type *) cast as a pointer to structure.

Here, the macro container_of the mystery is over.

** There is a point worth considering: ** Since __mptr = (ptr), why not just use the parameters passed to reduce ptr, but seemingly "superfluous" second line assigns the value of ptr __mptr, then __mptr to cut it?

The answer to incoming parameters of a type of security check. A macro is to be processed by the preprocessor at compile time. Preprocessor to do a simple text replacement, does not make any type checking, which may lead us in the preparation of the code, due to the carelessness errors. For example, container _of (ptr, type, member) has three parameters, if the incoming ptr, we due to the carelessness, the wrong ptr a pointer passed, found that the program may run correctly, but the result is wrong . This time to increase the security of the code, in order to be able to have a little bit of type safety checks, so the kernel designers in the definition of container _of when, in the second line defines a line of code to add the type of security checks, it will be wrong when you pass a pointer, a pop-up warning, this warning tells us that there are cases where the type is not compatible in this place, so that we can go before running it again check parameters, thus avoiding a BUG.

Epilogue

So far, we have clearly know the role of the container_of. Now we are back to the original starting point --- list _entry (), will understand why it is called the kernel of the first macro.

Published 40 original articles · won praise 18 · views 7582

Guess you like

Origin blog.csdn.net/weixin_44395686/article/details/99365705