In-depth analysis of the data structure of the Linux kernel linked list

Source: Internet
Author: User
Article title: analyze the data structure of the Linux kernel linked list in depth. Linux is a technology channel of the IT lab in China. Includes basic categories such as desktop applications, Linux system management, kernel research, embedded systems, and open source.
   I. data structure of linked lists
A linked list is a commonly used data structure for organizing ordered data. it connects a series of data nodes into a data link through a pointer. it is an important implementation method for linear tables. Compared with arrays, the linked list is more dynamic. when creating a linked list, you do not need to know the total amount of data in advance. you can randomly allocate space and efficiently insert or delete data anywhere in the linked list. The overhead of the linked list is mainly the access sequence and the space loss of the organizational chain.
  
Generally, the linked list data structure should contain at least two domains: data domains and pointer domains. data domains are used to store data and pointer domains are used to establish connections with the next node. According to the organization of the pointer field and the connection form between nodes, the linked list can be divided into single-chain tables, double-chain tables, circular linked lists, and other types. The following lists the common linked list types:
  
1. single-chain table
  
   Single-chain table
    
A single-chain table is the simplest type of linked list. it has only one pointer field pointing to the next node. Therefore, the traversal of a single-chain table can only start from the beginning to the end (usually a NULL pointer).
  
2. double-stranded table
  
   Double-linked table
    
By designing two pointer fields, the dual-chain table can be traversed in two directions, which is different from the single-chain table. If the dependency between the predecessor and the successor is disrupted, a "binary tree" can be formed "; if the frontend of the first node points to the end node of the linked list and the successor of the end node points to the first node (the dotted line in 2), a circular linked list is formed. if more pointer fields are designed, to form a variety of complex tree data structures.
  
3. circular linked list
The cyclic linked list feature that the tail node points to the first node. The double-loop linked list has been given in the previous section. It is characterized by any node and any data in the two directions can be found in the linked list. If the precursor pointer is removed, it is a single-loop linked list.
  
A large amount of linked list structures are used in the Linux kernel to organize data, including the device list and data organization in various functional modules. Most of these linked lists are implemented in [include/linux/list. h. The subsequent sections of this article will detail the organization and use of this data structure through examples.
  
   II. implementation of the data structure of Linux 2.6 Kernel Linked list
Although the 2.6 kernel is used as the basis for the explanation, the linked list structure in the 2.4 kernel is actually no different from that in the 2.6 kernel. The difference is that 2.6 expands the data structure of two linked lists: the read copy update (rcu) and the HASH linked list (hlist ). These two extensions are based on the most basic list structure. Therefore, this article mainly introduces the basic linked list structure, and then briefly introduces rcu and hlist.
  
The definition of the linked list data structure is very simple (excerpted from [include/linux/list. h]. all the following code, unless described, is taken from this file ):
  
Struct list_head {
Struct list_head * next, * prev;
};
  
The list_head structure contains two pointers to the list_head structure, prev and next. it can be seen that the linked list of the kernel has the double-chain Table function. In fact, it is usually organized into a double-cycle linked list.
  
Unlike the double-chain table structure model introduced in section 1, the list_head has no data field. In the Linux kernel linked list, instead of containing data in the linked list structure, it contains linked list nodes in the data structure.
  
In data structure textbooks, the classic definition of linked lists is usually as follows (using a single-chain table as an example ):
  
Struct list_node {
Struct list_node * next;
ElemTypedata;
};
  
Because of ElemType, each data item type needs to define its own linked list structure. Experienced C ++ programmers should know that The C ++ Template is used to abstract the linked list operation interface unrelated to the data item type.
  
In the linux kernel linked list, data that needs to be organized by the linked list usually contains a struct list_head member, for example, in [include/linux/netfilter. h] defines an nf_sockopt_ops structure to describe the getsockopt/setsockopt interface prepared by Netfilter for a protocol family. there is a member (struct list_head list, the nf_sockopt_ops structure of each protocol family is organized in a linked list through this list member. the header is defined in [net/core/netfilter. in c], nf_sockopts (struct list_head ). We can see that this generic linked list structure avoids the trouble of defining your own linked list for each data item type. Linux is simple and practical, and does not seek perfection or standard style, which is fully embodied here.
  
   Nf_sockopts linked list
    
   III. Linked List operation interface
1. Declaration and initialization
In fact, Linux only defines linked list nodes and does not specifically define the linked list header. how can a linked list structure be created? Let's take a look at the macro LIST_HEAD:
  
# Define LIST_HEAD_INIT (name) {& (name), & (name )}
# Define LIST_HEAD (name) struct list_head name = LIST_HEAD_INIT (name)
  
When we declare a chain table header named nf_sockopts with LIST_HEAD (nf_sockopts), its next and prev pointers are initialized to point to ourselves, so that we have an empty chain table, in Linux, the next of the header pointer is used to determine whether the linked list is empty:
  
Static inline int list_empty (const struct list_head * head)
{
Return head-> next = head;
}
  
In addition to initializing a linked list with the LIST_HEAD () macro, Linux also provides an INIT_LIST_HEAD macro for initializing the linked list at runtime:
  
# Define INIT_LIST_HEAD (ptr) do {(ptr)-> next = (ptr); (ptr)-> prev = (ptr);} while (0)
  
We use INIT_LIST_HEAD (& nf_sockopts) to use it.
  
2. Insert/delete/merge
A) insert
  
Two insert operations are available for the linked list: insert at the header and insert at the end of the table. Linux provides two interfaces for this purpose:
  
Static inline void list_add (struct list_head * new, struct list_head * head );
Static inline void list_add_tail (struct list_head * new, struct list_head * head );
  
Because the Linux linked list is a cyclic table, and the next and prev of the header point to the first and last nodes in the linked list respectively, the list_add and list_add_tail are not very different. In fact, Linux uses
  
_ List_add (new, head, head-> next );
  
And
  
_ List_add (new, head-> prev, head );
  
To implement two interfaces. it can be seen that after the header is inserted after the head, and after the end of the table is inserted after the head-> prev.
  
Suppose there is a new nf_sockopt_ops structure variable new_sockopt which needs to be added to the nf_sockopts linked list header. we should do this:
  
List_add (& new_sockopt.list, & nf_sockopts );
  
From this we can see that the nf_sockopts linked list records not the address of new_sockopt, but the address of the list element. How can I access new_sockopt through a linked list? The following is a detailed introduction.
  
B) Delete
  
Static inline void list_del (struct list_head * entry );
  
To delete the new_sockopt entry added to the nf_sockopts linked list, perform the following operations:
  
List_del (& new_sockopt.list );
  
The excluded new_sockopt.list, prev, and next pointers are set to two special values: LIST_POSITION2 and LIST_POSITION1, respectively, this setting ensures that node items not in the linked list are inaccessible-access to LIST_POSITION1 and LIST_POSITION2 will cause page faults. Correspondingly, after the list_del_init () function resolves the node from the linked list, it calls LIST_INIT_HEAD () to set the node to the empty chain state.
  
C) migration
  
Linux allows you to move a node that originally belongs to a linked list to another linked list. There are two types of nodes inserted into the new linked list:
  
Static inline void list_move (struct list_head * list, struct list_head * head );
Static inline void list_move_tail (struct list_head * list, struct list_head * head );
  
For example, list_move (& new_sockopt.list, & nf_sockopts) will delete new_sockopt from its linked list and link it to the header of nf_sockopts.
  
D) merge
  
In addition to the insert and delete operations on nodes, the Linux linked list also provides the insert function for the entire linked list:
  
Static inline void list_splice (struct list_head * list, struct list_head * head );
  
Assume that there are two linked lists, list1 and list2 (both the struct list_head variables). when list_splice (& list1, & list2) is called, as long as list1 is not empty, the contents of the list1 linked list will be mounted to the list2 linked list, located between list2 and list2.next (the first node of the original list2 table. The new list2 linked list takes the first node of the original list1 table as the first node, while the last node remains unchanged. (The virtual arrow is the next pointer ):
  
   List_splice (& list1, & list2)
    
After list1 is mounted to list2, the next and prev of list1 as the original header pointer still point to the original node. to avoid confusion, Linux provides a list_splice_init () function:
  
Static inline void list_splice_init (struct list_head * list, struct list_head * head );
  
After merging the list to the head linked list, this function calls INIT_LIST_HEAD (list) to set the list to a null chain.
  
3. Traverse
Traversal is one of the most common operations of a linked list. to facilitate the core application of a traversal table, the Linux linked list abstracts the traversal operation into several macros. Before introducing rmacro, let's take a look at how to access the data items we actually need from the linked list.
  
A) from linked list nodes to data item variables
  
We know that Linux linked lists only store the address of the list_head member variable in the data item structure. how can we access the node data of the list_head member as its owner? Linux provides a list_entry (ptr, type, member)
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.