Using the linked list to manage objects in the kernel is worth learning.
The linked list is often used in the kernel to manage objects. Let's take a look at the definition of the linked list in the kernel.
Struct list_head {
Struct list_head * Next, * Prev;
};
Generally, this data structure is embedded into other data structures, so that the kernel can manage the new data structure through a linked list. Let's look at an example:
Struct example {
Member;
Struct list_head list;
Member B;
};
1. Definition and initialization of linked lists
You can define and initialize a linked list header node in two ways. For example, if you want to define a linked list header node mylist, you can do this:
① List_head (mylist); // use the list_head macro to define and initialize a linked list
You can also do this:
② Struct list_head mylist; // defines a linked list
Init_list_head (& mylist); // use the init_list_head function to initialize the linked list.
The method ① Is a little simpler. Let's analyze the list_head macro first:
# Define list_head_init (name) {& (name), & (name )}
# Define list_head (name )/
Struct list_head name = list_head_init (name)
It is easy to see that list_head (mylist); will be extended:
Struct list_head mylist = {& (mylist), & (mylist )};
The list_head structure has only two members: Next and Prev. From the code above, we can see that both next and Prev are assigned the address of mylist, that is, the initial
After the conversion, both next and Prev point to their own.
In most cases, list_head is embedded into other data structures, such as list members in the example structure above. How can we initialize list members? Call the init_list_head function:
Struct example test;
Init_list_head (& test. List );
This function points the prev and next pointers of list members to itself.
We can see that during initialization, the linked list node points both Prev and next to itself. Note: initialization of the linked list is very important, because if you use an uninitialized linked list node, it is likely to cause kernel exceptions. For example, after calling the list_del function for a linked list node, you can perform operations on the node. Analyzed later :)
2. Common Operations on linked lists
Common Operations on linked lists are addition, deletion, and traversal. Of course, the kernel also provides many other operations, such as replacing a node, moving a node to the end of the linked list, and so on. These operations are completed by calling basic operations such as adding and deleting.
① Added: list_add and list_add_tail
You can call list_add to insert a new linked list node to the end of a known node;
You can call list_add_tail to insert a new linked list node to the front of a known node;
The following describes their specific implementations. They all call the same function _ list_add with different parameters:
Static inline void _ list_add (struct list_head * New,
Struct list_head * Prev,
Struct list_head * Next)
{
Next-> Prev = new;
New-> next = next;
New-> Prev = Prev;
Prev-> next = new;
}
This function inserts the new node into the prev node and next;
Static inline void list_add (struct list_head * New, struct list_head * head)
{
_ List_add (new, head, head-> next );
}
The list_add function calls _ list_add with the parameters new, head, head-> next and inserts the new node between head and head-> next, that is, insert the new node to the end of a specific known node head;
Static inline void list_add_tail (struct list_head * New, struct list_head * head)
{
_ List_add (new, head-> Prev, head );
}
The list_add_tail function calls _ list_add with the parameters new, head-> Prev, and head to insert the new node into the header-> Prev and head, that is, insert the new node to the front of a specific known node head.
With list_add and list_add_tail, we can easily implement the stack (list_add) and queue (list_add_tail). In the last section of this article, we will perform a detailed analysis.
② Delete: list_del and list_del_init
Call the list_del function to delete a node in the linked list;
Call the list_del_init function to delete a node in the linked list and initialize the deleted node (that is, to direct the prev and next of the deleted node to itself );
The following describes their specific implementations. They all call the same function _ list_del:
Static inline void _ list_del (struct list_head * Prev, struct list_head * Next)
{
Next-> Prev = Prev;
Prev-> next = next;
}
The actual function is to point the prev and next nodes to each other;
Static inline void list_del (struct list_head * entry)
{
_ List_del (Entry-> Prev, entry-> next );
Entry-> next = list_python1;
Entry-> Prev = list_python2;
}
In this function, the _ list_del function is called with the entry-> Prev and entry-> next parameters, so that the front and back nodes of the entry node direct to each other by bypassing the entry, then, point the front and back pointer of the entry node to list_python1 and list_python2 to delete the entry node. In this function, list_python1 and list_python2 are a bit confusing, because after we delete the entry, we should point the prev and next of the entry to null, but this is not the case here. The reason remains to be investigated.
Static inline void list_del_init (struct list_head * entry)
{
_ List_del (Entry-> Prev, entry-> next );
Init_list_head (entry );
}
Unlike list_del, after list_del_init deletes the entry node, it also initializes the entry node so that both the prev and next of the entry node point to itself.
3. Several important macros
The kernel provides a set of macros to facilitate the management of the linked list. I will only introduce the list that I have encountered so far, but it may be rare because I have limited access to it, in the future, if you encounter other problems, they will be added. The following is an analysis :)
① List_entry
As mentioned above, the list_head structure is usually embedded into other data structures, so that the kernel can manage these data structures through a linked list. Assume that the address PTR (struct list_head * PTR) of the List member of an object of the example type is known. How can we obtain the address of this example object through PTR? The answer is obvious: Use the container_of macro. However, in this case, we should use the list_entry macro to implement the container_of macro function, because it is easier to understand. In fact, the list_entry macro is very simple: # define
List_entry (PTR, type, member) container_of (PTR, type, member )......
In the above case, we can use list_entry (PTR, struct example, list); to obtain the pointer of the example object.
② List_for_each_entry
An important operation of the linked list is to traverse the linked list for a certain purpose, such as counting the number of linked list nodes. Let's take a look at the definition of the macro in the kernel:
# Define list_for_each_entry (Pos, Head, member )/
For (Pos = list_entry (head)-> next, typeof (* POS), member );/
Prefetch (POS-> member. Next), & Pos-> member! = (Head );/
Pos = list_entry (POS-> member. Next, typeof (* POS), member ))
Here, POS is a pointer to the host structure, which is an iteration variable in the for loop; head is the head pointer of the linked list to be traversed; member is the name of the list_head member in the host structure.
The linked list is one of the most frequently used data structures in the kernel. The latest kernel (in 2.6.39) is defined in <Linux/types. h>.
Struct list_head {
Struct list_head * Next, * Prev;
};
In <Linux/list. h>, some common operation macros and inline functions for linked lists are defined:
# Define list_head_init (name) {& (name), & (name )}
# Define list_head (name )/
Struct list_head name = list_head_init (name) // defines a linked list and points it to itself through initialization. A linked list is initialized statically.
Static inline void init_list_head (struct list_head * List) // initialize a linked list dynamically, which has the same effect as the preceding one.
{
List-> next = List;
List-> Prev = List;
}
# Ifndef config_debug_list
Static inline void _ list_add (struct list_head * New,
Struct list_head * Prev,
Struct list_head * Next)
{
Next-> Prev = new;
New-> next = next;
New-> Prev = Prev;
Prev-> next = new;
}
# Else
Extern void _ list_add (struct list_head * New,
Struct list_head * Prev,
Struct list_head * Next );
# Endif
Void _ list_add (struct list_head * New,
Struct list_head * Prev,
Struct list_head * Next)
{
Warn (next-> Prev! = Prev,
"List_add upload uption. Next-> Prev shocould be"
"Prev (% P), but was % P. (next = % P)./N ",
Prev, next-> Prev, next );
Warn (prev-> next! = Next,
"List_add upload uption. Prev-> next shocould be"
"Next (% P), but was % P. (prev = % P)./N ",
Next, Prev-> next, Prev );
Next-> Prev = new;
New-> next = next;
New-> Prev = Prev;
Prev-> next = new;
}
Export_symbol (_ list_add );
Struct list_head * Prev,
Struct list_head * Next );
# Endif
// The following is the function definition when config_debug_list is defined. Its function is to link new to Prev and next.
Void _ list_add (struct list_head * New,
Struct list_head * Prev,
Struct list_head * Next)
{
Warn (next-> Prev! = Prev,
"List_add upload uption. Next-> Prev shocould be"
"Prev (% P), but was % P. (next = % P)./N ",
Prev, next-> Prev, next );
Warn (prev-> next! = Next,
"List_add upload uption. Prev-> next shocould be"
"Next (% P), but was % P. (prev = % P)./N ",
Next, Prev-> next, Prev );
Next-> Prev = new;
New-> next = next;
New-> Prev = Prev;
Prev-> next = new;
}
Export_symbol (_ list_add );
// The encapsulation of the following function, which is used between inserting new into head and head-> next
Static inline void list_add (struct list_head * New, struct list_head * head)
{
_ List_add (new, head, head-> next );
}
// Encapsulate the following functions, and insert new into head-> Prev and head
Static inline void list_add_tail (struct list_head * New, struct list_head * head)
{
_ List_add (new, head-> Prev, head );
}
// The following function is used to connect the prev and next linked lists.
Static inline void _ list_del (struct list_head * Prev, struct list_head * Next)
{
Next-> Prev = Prev;
Prev-> next = next;
}
// The following function points a linked list to the forward and backward directions, so that it itself is out of the linked list, so it is disconnected from the linked list.
# Ifndef config_debug_list
Static inline void _ list_del_entry (struct list_head * entry)
{
_ List_del (Entry-> Prev, entry-> next );
}
Static inline void list_del (struct list_head * entry)
{
_ List_del (Entry-> Prev, entry-> next );
Entry-> next = list_python1;
Entry-> Prev = list_python2;
}
# Else
Extern void _ list_del_entry (struct list_head * entry );
Extern void list_del (struct list_head * entry );
# Endif
// New replaces the old position in the linked list where the old is located, but the old position also points to the previous position.
Static inline void list_replace (struct list_head * old,
Struct list_head * New)
{
New-> next = old-> next;
New-> next-> Prev = new;
New-> Prev = old-> Prev;
New-> Prev-> next = new;
}
// New replaces the old position in the linked list where the old is located, and makes the old itself
Static inline void list_replace_init (struct list_head * old,
Struct list_head * New)
{
List_replace (old, new );
Init_list_head (old );
}
// Point the entry to the linked list node and disconnect it from the linked list and direct it to itself.
Static inline void list_del_init (struct list_head * entry)
{
_ List_del_entry (entry );
Init_list_head (entry );
}
// Disconnect its own linked list from the list and import it to the linked list of the head.
Static inline void list_move (struct list_head * List, struct list_head * head)
{
_ List_del_entry (list );
List_add (list, head );
}
// It is the same as the above function, except that it is opposite to the position before and after adding
Static inline void list_move_tail (struct list_head * List,
Struct list_head * head)
{
_ List_del_entry (list );
List_add_tail (list, head );
}
// Determine whether the list is a list with no nodes starting with the head
Static inline int list_is_last (const struct list_head * List,
Const struct list_head * head)
{
Return list-> next = head;
}
// Judge whether a linked list is empty
Static inline int list_empty (const struct list_head * head)
{
Return head-> next = head;
}
// Strictly judge the empty linked list and make judgments on both the front and back points
Static inline int list_empty_careful (const struct list_head * head)
{
Struct list_head * Next = head-> next;
Return (next = head) & (next = head-> PREV );
}
// If the linked list is not empty, move the next of the head to the header-> between Prev and head
Static inline void list_rotate_left (struct list_head * head)
{
Struct list_head * first;
If (! List_empty (head )){
First = head-> next;
List_move_tail (first, head );
}
}
// Determine whether the linked list has only one node
Static inline int list_is_singular (const struct list_head * head)
{
Return! List_empty (head) & (Head-> next = head-> PREV );
}