Article Title: describes the two-way circular linked list of Linux kernel. 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.
1. Traditional Implementation of double-loop linked list:
In the traditional implementation of double-loop linked lists, if you create a double-loop linked list with a data structure, you can add two (pointing to this type of object) to the Type Definition of the data structure) pointers: next and prev. For example:
QUOTE:
Typedef struct foo {
...
Struct foo * prev;
Struct foo * next;
...
} Foo_t; |
The corresponding node structure, empty double-cycle linked list, and non-empty double-cycle linked list are provided here.
2. Dual-loop linked list implementation in Linux Kernel
In the Linux kernel, a large number of data structures require a dual-loop linked list, such as processes, files, modules, and pages. If you use the traditional Implementation Method of Double-loop linked lists, you need to maintain their respective linked lists for these data structures and design operation functions such as insert and delete for each linked list. Because the next and prev pointers used to maintain the linked list point to the corresponding type of objects, the linked list operation function of a data structure cannot be used to operate the linked list of other data structures.
In the include/Linux/list. h file of the linux source code tree, a type-independent dual-loop linked list implementation method is adopted. The idea is to extract the pointer prev and next from the specific data structure to form a general "double-stranded table" Data Structure list_head. If you need to construct a specific linked list of a certain type of objects, define a member of The list_head type in its structure (known as the host data structure) and connect these objects through this member, form the required linked list and operate it through the universal linked list function. The advantage is that you only need to write a universal linked list function to construct and operate a linked list of different objects, instead of writing a special function for each list of each type of objects, achieving code reuse.
List_head Structure
QUOTE:
----------- Struct list_head {} and initialization macro ---------
Struct list_head
{
Struct list_head * next, * prev;
}; |
When an independent variable is defined using this type, it is the header node;
When it is a member of a struct, it is a common node.
Although the form is the same, the expression has different meanings. Should they be defined as two types: list_head and list_node ??? Unable to separate, empty linked list points to yourself
The list_head member acts as a "connector" to link the host data structure. As shown in:
In the Linux kernel, the dual-loop linked list is implemented as follows:
1. variables of the list_head type are embedded into the host data structure as a member;
2. The linked list structure can be placed anywhere in the host structure;
3. Any name can be used for the linked list structure;
4. The host structure can have multiple linked list structures;
5. Use the members in list_head and corresponding processing functions to traverse the linked list;
6. If you want to get a pointer to the host structure, use list_entry to calculate it.
3. Definition and initialization
QUOTE:
-- LIST_HEAD_INIT () -- LIST_HEAD () -- INIT_LIST_HEAD ()------
# Define LIST_HEAD_INIT (name) {& (name), & (name )}
# Define LIST_HEAD (name )\
Struct list_head name = LIST_HEAD_INIT (name) |
Note that each dual-loop linked list in Linux has a chain table header, which is also a node, but is not embedded in the host data structure, that is, you cannot use the linked list header to locate the corresponding host structure, but you can obtain the virtual host structure pointer.
The LIST_HEAD () macro can define the head of the linked list at the same time and initialize the double loop linked list to be empty.
Static definition of a list_head type variable, which must be a header node. Name is a variable of the type struct list_head {}, and & (name) is the address of the struct variable. Use the start address of the name struct variable to initialize the struct variable.
QUOTE:
# Define INIT_LIST_HEAD (ptr) do {\
(Ptr)-> next = (ptr); (ptr)-> prev = (ptr );\
} While (0) |
Dynamically Initialize an existing list_head object. ptr is a pointer to a struct. This can initialize the stack and the list_head object defined in the Global zone. When ptr is used, Parentheses (ptr) are used to avoid macro expansion exceptions caused by ptr. This macro is rarely used to dynamically initialize embedded list objects. It is mainly used to merge or delete linked lists and re-initialize the header. If you apply for this linked list header in the heap, call the INIT_LIST_HEAD () macro to initialize the linked list node and direct the next and prev pointers to itself. Then we construct an empty double loop linked list.
2.6 The kernel version of inline functions is as follows:
QUOTE:
Static inline void INIT_LIST_HEAD (struct list_head * list)
{
List-> next = list;
List-> prev = list;
} |
At this time, the parameter has a clear type information struct list_head, and it can be seen that it is a pointer, the list does not need to be as () as in the macro, even if the parameter is an expression, it is also evaluated before passed in as a parameter. Inline functions have strict parameter type checks and do not cause any exceptions caused by Macro function extensions. However, the running efficiency and space efficiency are consistent with those of macro functions.