Linux kernel Analysis--double-linked list of data structures in the kernel (i) __ajax

Source: Internet
Author: User
Tags data structures hash

About the data structures used in the kernel this series will have five articles,

Introduced separately

Linked list

Queue

Hash

Mapping

Red and black Trees
first, the introduction of the core linked list

The linked list defined in the kernel is a two-way list, in the previous article--libevent source code Analysis--queue.h in the understanding of tail Q_Q Ueue describes how to define a linked list queue in FreeBSD, and the definition in the Linux kernel is still different, but the same classic.

The code for the definition of a linked list in the kernel is located at: include/linux/list.h. There is a comment on each function in the List.h file, which is not described in detail here.        In fact, just begin to understand a commonly used linked list operations (append, delete, traverse) implementation methods, the other methods are basically based on these common operations. Before introducing the definition of a linked list in the kernel, it is different to recall the way the list is defined in the data structure.

The general two-way linked list is generally the following structure, there is a separate head node (node). In addition to containing the necessary data, there are 2 pointers (Pre,next) pre pointers to the previous node (node), and the next pointer points to the last node (node) header ( Head) point to the last node of the list, next pointer to the last node

(Thanks to the original author)

One of the biggest drawbacks of a traditional list is that it is not good for Tonghua, because the DATA1,DATA2 in each node and so on is indeterminate (whether it's a number or a type).

The list of Linux is a clever solution to this problem, the list of Linux is not to save the user data in the linked list node, but to save the linked list node in the user data.

The Linux linked list node has only 2 pointers (pre and next), so that the nodes of the linked list will be independent of the user data and facilitate the common operation of the linked list. As shown in the following illustration:

The very standard of this picture, try to think it over.

The definition in Include/linxu/list.h is also very simple:

struct List_head {     struct list_head *next, *prev;
 21};
When used, the structure is defined by itself, but the data in the structure except the user is the structure. This allows you to construct a two-way list of your own definition.

In understanding the basic content see concrete implementation, only know the data member list address, how to access themselves and other members.

The biggest problem in the Linux list is how to get the user data through the node of the linked list.

Unlike traditional lists, Linux's linked list node (node) does not contain user data1,data2. Let's get down to business:

You can see this code in the Include/linux/list.h header file.

#define List_entry (Ptr,type,member)    /
    container_of (Ptr,type,member)
Where container_of this macro is in the/include/linux/kernel.h header file.

#define CONTAINER_OF (PTR, type, member) ({          \
648     const typeof ((type *) 0)->member) *__mptr = (PTR);    \
649     (Type *) ((char *) __mptr-offsetof (Type,member));})

The type in this is generally a structure, that is, a structure that contains user data and linked list nodes.

PTR is a pointer to a linked list node in type

Member is the name used to define the linked list node in type

There are a few things to explain about this macro explanation,

1, typeof (Type), which is a macro that returns the type of one, for example: int A; typeof (a) B; equivalent to int b;

2, Offsetof (TYPE,MEMBER) macro It is defined in Include/linx/stddef.h, as follows:
#define OFFSETOF (Type, member) ((size_t) & ((type *) 0)->member)
This macro returns the offset of member in type, type is a struct, for example:
typeof (List_head,next); returns 0, which returns the offset relative to the structure's starting address. There may be a question as to why 0 is coerced into a pointer of one type, and then the pointer points to a member of that type, and the address of the member in the pointer is the offset of that member in that type.

This situation is generally used to get the offset of a member in the structure body. Since the first address starts at 0, the address of the struct member is the value of his offset. Probably not quite clear, then what the pointer is, is an address, the contents of the pointer is the first address of a variable, the 0 strong to the pointer type, that is, the pointer value is zero, and this value is the first address of the object referred to. (offset + First address = Member address, where only the first address is changed to 0, then the member address is the offset.) )

You can use a simple example to illustrate:

struct student
{
    int id;
    char* name;
    struct List_head list;
<ul><li>type is struct student</li><li>ptr is a pointer to the Stuct list, which is a pointer to the member type </li>< Li>member is the list
</li></ul>
The following figure illustrates this macro as an example of STURCT student:

First you need to know ((TYPE *) 0) that the address 0 is converted to type

Because the address of type is 0, so ((TYPE *) 0)->member is the difference between the address of member and the type address, as shown in the following illustration:



3, use typeof ((type *) 0)->member to define pointer __ptr, instead of: Const typeof (member) *__ptr=ptr;.
In fact, this is very simple, because member is the structure of the members, only through the structure to access.

4. In this sentence (type *) ((char *) __mptr-offsetof (type,member)); Before the minus sign is the member's address, minus is the member in the structure of the offset, subtraction is the first address of this structure.

Access to data in a linked list:

In file include/linux/list.h, there is code to access the linked list data

#define LIST_FOR_EACH_ENTRY (POS, head, member) for
    (Pos=list_entry (head)->next,typeof (*pos), member);

#define LIST_FOR_EACH_ENTRY (POS, head, member)
For (Pos=list_entry (head)->next,typeof (*pos), member);
From the use above, replacing List_entry macros and CONTAINER_OF macros becomes the following:
pos= ({const typeof (typeof (*pos) 0)->member) *__ptr= (head)->next;

pos= ({const typeof (typeof (*pos) 0)->member) *__ptr= (head)->next;
            (typeof (*pos) *) ((char *) __ptr-offsetof (typeof (typeof (*pos)), member);});


Second, there is also a linked list, as a two-way linked list use

struct hlist_head{
    struct hlist_node *first;
struct hlist_node{
    struct hlist_node *next, **pprev;
This doubly linked list is not really a two-way list, because the header has only one first field and why it is designed. Comments in the Code explain: in order to save memory, especially suitable as a hash table conflict chain, but the hash table is very large, then the table head savings of memory is quite objective, although each table head to save only one pointer.
At the same time, the inconsistency of the head of the table will also bring difficulties in the operation of the linked list, it is obvious that the table header and the first Data node to insert between the node needs special treatment, which is why the design of the two-level pointer pprev reason. Look at the code.
static inline void Hlist_add_before (struct hlist_node *n,struct hlist_node *next)
{
    n->pprev=next-> Pprev;
    n->next=next;
    next->pprev=&n->next;
    * (N->pprev) =n;
}
Explanation: pointer n points to the new node, and pointer next points to the node that will be inserted into the new node before it.
Looking at the code above, you can see the power of the level two pointer pprev. Did you see that when next is the first data node, the insertion here is to insert a node between the header and the first Data node, but no special processing is required. Instead, a uniform use of * (N->pprev) is used to access the pointer field of the predecessor (next in the normal node and first in the table header). See this and the previous article explained the tail Q_Q ueue is not very similar. In fact, this kind of data structure is also explained in FreeBSD.

The designer of the Linux list for excellence (because List.h is not signed, so it is likely that Linus Torvalds) that the dual-head (Next, prev) Double linked list is "too wasteful" for the hash table, Thus, a set of hlist data structure for hash table application is designed--single pointer double loop chain list, as can be seen from the above figure, the Hlist table header has only one pointer to the first node, and no pointer to the tail node. This reduces the amount of space consumed by a table header stored in a potentially massive hash table.

Because the data structure of the header and the node is different, the previous method does not work if the insert operation occurs between the header and the first node: the first pointer of the header must be modified to point to the newly inserted node, but it cannot use a uniform description like List_add (). To do this, the prev of the Hlist node no longer refers to a pointer to a node ahead, but rather to the next (the first) pointer (struct List_head **pprev) in the forward node (possibly the header), which can be inserted in the table header by a consistent "* (Node->pprev) "accesses and modifies the next (or a) pointer of the predecessor node.


Related Article

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.