Interesting Data Structure-linked list in Linux Kernel

Source: Internet
Author: User
There are many types of linked lists in the Linux kernel. If each type of linked list is represented by a separate data structure, you need to implement a set of primitive operations for each linked list, including initialization, insertion, and deletion. Therefore, the Linux kernel defines an interesting data structure: list_head Struct List_head {
Struct List_head * Next, * Prev;
};

At first glance, this definition seems very common, but it is wonderful in general.

Generally, we always embed data into the node of the linked list, similar to the following definition method: Struct List_node {
Data_type data;
List_node * Next, * Prev;
}

As follows:

However, the opposite is true here. The linked list node is embedded into the data structure: Struct Data_type {
Data;
List_head;
}

As follows (a dummy element is added ):

 

In this type of linked list, all basic operations on the linked list are performed on the list_head data structure, rather than the data structure containing the list_head. Therefore, no matter what data, the linked list operations are unified. Now we have a problem, because all the pointers involved in linked list operations point to the list_head data structure, not the contained data structure, so how can we get the address containing the data structure from the list_head address? Let's look at the macro list_entry (P, T, m) in the Linux kernel: # Define List_entry (PTR, type, member )\
Container_of (PTR, type, member)

Tracking container_of macro:

# Define Container_of (PTR, type, member )({\
Const   Typeof (Type * ) 0 ) -> Member) * _ Mptr = (PTR );\
(Type * )(( Char   * ) _ Mptr - Offsetof (type, member ));})

Here, offsetof does not need to be tracked. We can also understand the meaning of this macro. First, let's briefly describe the three macro parameters. PTR is a pointer to the list_head data structure. type is the type of the container data structure, and member is the name of list_head in type. Let's look at the following example.Code:

Struct Data {
Xxx;
List_head list1;
List_head list2;
Xxx;
};
Struct Data vardat = {Initialization };
List_head * P =   & Vardat. list1;
List_head * Q =   & Vardat. list2;

List_entry (p,StructData, list1)= &Vardat;
List_entry (Q,StructData, list2)= &Vardat;

 

As shown in the preceding example, vardat can be mounted to two (or more) linked lists at the same time. list1 is a node on the first linked list and list2 is the node on the second linked list, vardat can be obtained from both & list1 and & list2, and the above problem is solved.

I skipped the offsetof Macro. I believe many readers will be interested in this macro. Now let's take a look at how this macro is implemented. Tracking this macro will find two definitions, one is _ compiler_offsetof (a, B), continue tracking to get _ builtin_offsetof (a, B), this will not be viewed; let's look at another definition: # Define Offsetof (type, member) (size_t) & (type *) 0)-> Member)

After reading it, I suddenly realized that it was so simple that the member of a type pointing to 0 would naturally be offset!

Summary of the advantages of this linked list: (1) the basic operations on all linked lists are based on the list_head pointer. Therefore, when adding a type, you do not need to write the basic operation function of the linked list repeatedly (2) A container data structure can contain multiple list_head members, in this way, it can be mounted to multiple different linked lists at the same time. For example, in the Linux kernel, the process data structure (task_struct) will be mounted to multiple linked lists, such as the task linked list and priority linked list. More instructions will be given below. Let's take a look at the basic chain table operations provided in the Linux kernel (the data types not described are list_head *): List_add ( New , Head ); // Insert new to the end of the head Element
List_add_tail ( New , Head ); // Yes, you don't need to explain the difference above, but the head here is the real head of the linked list.
List_del (entry ); // Delete entry Node
List_empty (head ); // Check whether the linked list is empty.
List_entry (PTR, type, member ); // Previously explained
List_for_each (Pos, head ); // Traverse the list. Each loop returns the list_head pointer of the node through POS.
// Below is the most useful!
List_for_each_entry (Pos, Head, member ); // The same as above, but the address returned by the POS is the container data structure.

Slow! A problem is found. type is required in list_entry. Why does list_for_each_entry not need it? Simple: POS is a pointer to the container data structure you give. In macro implementation, type is obtained using typeof (* POS!

Let's take a look at the data structure task_struct in Linux. It stores process information and only displays the content related to the linked list: Struct Task_struct {
// Xxxxxxx
Struct Hlist_head preempt_notifiers;
Struct List_head rcu_node_entry;
Struct List_head tasks;
Struct List_head children; /* List of my children */
Struct List_head sibling; /* Linkage in my parent's children list */
Struct List_head ptraced;
Struct List_head ptrace_entry;
Struct List_head thread_group;
// There are still a lot of lists, don't copy ......
}

Tasks is a linked list composed of all processes. to traverse all processes, you can use this macro:

# Define For_each_process (p )\
For (P =   & Init_task; (P = Next_task (p )) ! =   & Init_task ;)
# Define Next_task (p )\
List_entry (P) -> Tasks. Next, Struct Task_struct, tasks) Is this code cool ?! ------------------------ Question separation line ------------------------ the author encountered such a linked list implementation during the MIT operating system experiment Jos. The following is an example of use in the Code: Struct Frob
{
Int Frobozz;
List_entry (frob) frob_link; /* This contains the list element pointers */
};

List_head (frob_list, frob)/*Defines struct frob_list as a list of frob*/

StructFrob_list flist;/*Declare a frob list*/

list_init (& flist ); /* clear flist (globals are cleared anyway) */
flist = list_head_initializer ( & flist ); /* alternate way to clear flist */

If(List_empty (&Flist ))/*Check whether list is empty*/
Printf ("List is empty \ n");

Struct Frob * F = List_first ( & Flist ); /* F is first element in list */
F = List_next (F, frob_link ); /* Now F is next (second) element in list */
F = List_next (F, frob_link ); /* Now F is next (third) element in list */

For (F = List_first ( & Flist); f ! =   0 ; /* Iterate over elements in flist */
F = List_next (F, frob_link ))
Printf ( " F % d \ n " , F -> Frobozz );

List_foreach (F,&Flist, frob_link)/*Alternate way to say that*/
Printf ("F % d \ n", F->Frobozz );

F = List_next (list_first ( & Flist )); /* F is second element in list */
List_insert_after (f, g, frob_link ); /* Add g right after F in list */
List_remove (G, frob_link ); /* Remove g from List (can't insert twice !) */
List_insert_before (f, g, frob_link ); /* Add g right before F */
List_remove (G, frob_link ); /* Remove G again */
List_insert_head ( & Flist, G, frob_link ); /* Add G as first element in list */

 

It can be seen that the usage here is similar to that of the Linux kernel. You can use a set of macros to operate on various linked lists. However, after carefully reading the code of the related macros, we can find that, it is very different from list_head in Linux kernel. It is more like a template in C ++. during compilation, the corresponding code is generated for each list. Reprinted please indicate the source: Http://www.cnblogs.com/stephenjy/archive/2010/02/09/1666166.html

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.