Simple simulation of Linux kernel linked list (1)

Source: Internet
Author: User

Chapter II: Nonsense

An interesting question: Use a macro to define "FIND" to FIND the amount of data transfer between a variable in a struct and struc, as shown in figure

Struct student {int a; // FIND (struct student, a) equals 0 char B; // FIND (struct student, B) equals 4 double c ;};

Reference answer: # define FIND (type, member) (size_t) & (type *) 0)-> member)

I understand this (probably incorrect ):

(Type *) during the compilation process, an int temporary variable is loaded. Now, this temporary variable is converted into a pointer pointing to the hypothetical struct that exists at address 0, actually, there is no such thing.

(Type *) 0)-> member will access the variable of this struct,

& (Type *) 0)-> member obtains the address of this variable, because it is assumed that this struct is placed at the address 0, so the address of this variable is the offset from the struct,

(Size_t) & (type *) 0)-> member converts this address to size_t type (typedef unsigned int size_t) because the address is in the form of 0x00000004, converts it to an unsigned integer, that is, an offset.

The test is as follows:

# Include <iostream> # define FIND (type, member) (size_t) & (type *) 0)-> member) struct student {int; // FIND (struct student, a) equals 0 char B; // FIND (struct student, B) equals 4 double c ;}; using namespace std; int main (void) {cout <sizeof (size_t) <endl; cout <FIND (struct student, a) <endl <FIND (struct student, B) <endl <FIND (struct student, c) <endl; cin. get ();}View Code

The output is 4 0 4 8.

Certificate certificate ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Chapter 1: Common linked list

The normal linked list is not like this:

/* Indicates the structure of the Book */struct Book {int bkId; string bkName; struct Book * prev; // points to the previous node struct Book * next in the linked list; // point to the next node in the linked list };

Then write the specific operations on the struct Book structure, such as adding a node or deleting a node. Imagine: if there are dozens or even hundreds of linked lists, it's not a dead thing! How can programmers do this stupid thing to write a specific operation for a specific type of linked list? So there is the handsome Linux kernel linked list management method.

Certificate certificate ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Chapter 2: Linux kernel linked list

Linux Kernel linked list data structure is as follows: (in my m: \ linux-3.14.5 \ include \ linux types. h file)

struct list_head {    struct list_head *next, *prev;};

The normal linked list is to place the pointer pointing to the front and back nodes in the linked list node, but the Linux kernel is not doing this. It inserts the above struct list_head into the data structure, in this way, each struct list_head becomes a linked list node, where next points to the next struct list_hand, and prev refers to the previous struct list_hand. Is this very handsome? But it depends on how it is used. Now let's look at my struct Book. If you want to get a linked list of it, just like this:

Struct list_head {struct list_head * next, * prev ;};/* indicates the structure of the Book */struct Book {int bkId; string bkName; struct list_head list; // All Book struct form a linked list };

I copied it! The most important thing is to see how a linked list can be formed and how the linked list can be operated.

Certificate certificate ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Chapter 3: Create and initialize a Linux kernel linked list

  

Create a linked list, that is, create a linked list header, which is also a struct list_head, saving some Macros in the source code. (In my m: \ linux-3.14.5 \ include \ linux list. h file)

/* Initialize the linked list */static inline void INIT_LIST_HEAD (struct list_head * list) {list-> next = list; list-> prev = list ;}

In fact, it is to let the front and back pointers of the header point to itself. Currently, my code is as follows:

# Include <string> using std: string;/* linked list node */struct list_head {struct list_head * next, * prev ;}; /* indicates the structure of the Book */struct Book {int bkId; string bkName; struct list_head list; // All Book structures form a linked list }; /* initialize the linked list */static inline void INIT_LIST_HEAD (struct list_head * list) {list-> next = list; list-> prev = list ;}MyHead. h # include <iostream> # include "myList. h "using namespace std; int main (void) {struct list_head MyBkList; // create my linked list header INIT_LIST_HEAD (& MyBkList); // initialize this linked list cin. get ();}Main. cpp

Certificate certificate ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Chapter 4: Linux kernel linked list insertion

(1) Insert a node

The above just creates an empty linked list, and now we want to insert data.

The kernel provides a method to insert a new struct list_head node into two nodes:

/** Insert a new entry between two known consecutive entries.** This is only for internal list manipulation where we know* the prev/next entries already!*/static inline void __list_add(struct list_head *new1,struct list_head * prev,struct list_head * next){    next->prev = new1;    new1->next = next;    new1->prev = prev;    prev->next = new1;}

Sorry, I am a C ++ project, and new is the keyword of the language, so I changed new to new1, and the kernel source code is all new. This function is called by other inserted functions. In fact, it extracts the commonalities of the forward and backward insertion. This is really beautiful!

(2) tail insertion Node

/** * list_add_tail - add a new entry * @new: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */static inline void list_add_tail(struct list_head *new, struct list_head *head){    __list_add(new, head->prev, head);}

This method is to insert a node before the head node, and finally call _ list_add (new, head-> prev, head ); it indicates that a node is inserted between the previous node of the head and the head node.

Imagine: At first there was only one head node of the linked list, struct list_head head. head points to itself before and after it. Now call list_add_tail (struct list_head * new, struct list_head * head ), that is, _ list_add (new, head-> prev, head); The linked list forms two nodes, each pointing to each other:

Because this is a two-way linked list, we can see that new1 is added to the head. Call another function list_add_tail (struct list_head * new, struct list_head * head ):

So it can be seen that the head is the head of the team, and each modification is the head's prev pointer. Each time it is added at the end of the team, it is equivalent to a queue! The current Code is:

# Include <iostream> # include "myList. h "using namespace std; int main (void) {struct list_head MyBkList; // create my linked list header INIT_LIST_HEAD (& MyBkList ); // initialize this linked list/* Create a new Book struct */struct Book bk1; bk1.bkId = 1; bk1.bkName = "book1"; list_add_tail (& bk1.list, & MyBkList ); // Add the new Book 1 to the header node MyBkList followed by struct Book bk2; bk2.bkId = 2; bk2.bkName = "book2"; list_add_tail (& bk2.list, & MyBkList ); // Add book 2 between bk1 and MyBkList and read MyBkList as the header. The value is MyBkList-> bk1-> bk2 (according to the next pointer of the node, the next pointer of MyBkList is not changed, the prev pointer of MyBkList has changed) cin. get ();}Two nodes are added to the linked list.

(3) headers

There is also a head plug in the kernel, which is equivalent to every time it is inserted behind the head, that is, every time it changes the next pointer of the head, every time it is inserted in the front, it is equivalent to the stack! It's almost the same as above. I don't need to write much.

/** * list_add - add a new entry * @new: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */static inline void list_add(struct list_head *new, struct list_head *head){    __list_add(new, head, head->next);}

In fact, this is a better understanding. Every time a node is inserted before the head node and the head node.

Certificate certificate ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Chapter 4: From the list_head struct to the given data structure containing it

The above operations are aimed at the struct list_head operation. The insertion only transmits the struct list_head Member of the given struct to the function. But how can we obtain the struct containing the struct list_head? After all, when we really want to access the data we really care about. Okay, next step

Container_of () (in my m: \ linux-3.14.5 \ include \ linux kernel. h file)

/** * 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) );})


It has been clearly described that the struct contains members to obtain the struct containing this member. ptr is a pointer to the struct member, and type is the struct name containing the given member, member is the name of the specified member contained in the struct.

Typeof: an extension of g ++ to c/c ++ syntax. It is used to obtain the parameter type statically. If an error is reported under windows, I will migrate to linux, use g ++ for example:

Int a = 3;

Typeof (a) B = 4; // equivalent to int B = 4;

Offsetof: below, is the kernel source code defined macro (in my M: \ linux-3.14.5 \ include \ linux stddef. h file)

#undef offsetof#ifdef __compiler_offsetof#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)#else#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)#endif

The offsetoff (struct_t, member) macro is used to obtain the offset of the member in the struct_t type. Isn't the first part of the article about nonsense ???

Well, I understand the things in chapter 0 and I will probably know how to do it: Get the offset between the member and the type, and then get the given member Address minus this cheap. Isn't it the address of this struct? Here we mainly pay attention to some parameter types, because this macro is applicable to various struct types:

Typeof (type *) 0)-> member): obtains the specific type of the member name in the type struct, such as int or struct list_head, if it corresponds to my program, where type is struct Book and member is a list of the struct list_head type, the obtained result is struct list_head;

Const typeof (type *) 0)-> member) * _ mptr = (ptr); you can understand the corresponding program, that is, const struct list_head * _ mptr = ptr, which defines the type pointer of the member in a struct and assigns the ptr to it, when using this macro, the ptr passed is only an address of member in the form of 0xffffffff. If you do not know the specific type, member is just its own name. Now _ mptr has an ID address!

(Type *) (char *) _ mptr-offsetof (type, member )): the corresponding program is the list variable address in the struct Book minus the list offset in the struct Book. The obtained address is the structure address, which is forcibly converted to the struct Book type. Done! But I still don't understand why we need to convert ptr to _ mptr ......

Here, the minor Technique of the compiler technology is used to obtain the offset between the structure member and the structure, and then obtain the address of the Main Structure Variable Based on the address of the member variable.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.