GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing www.kernel.org — Linked Lists in Linux — The Linux Kernel documentation Linked Lists in Linux¶
Introduction¶Linked lists are one of the most basic data structures used in many programs. The Linux kernel implements several different flavours of linked lists. The purpose of this document is not to explain linked lists in general, but to show new kernel developers how to use the Linux kernel implementations of linked lists. Please note that while linked lists certainly are ubiquitous, they are rarely the best data structure to use in cases where a simple array doesn’t already suffice. In particular, due to their poor data locality, linked lists are a bad choice in situations where performance may be of consideration. Familiarizing oneself with other in-kernel generic data structures, especially for concurrent accesses, is highly encouraged. Linux implementation of doubly linked lists¶Linux’s linked list implementations can be used by including the header file
The doubly-linked list will likely be the most familiar to many readers. It’s a list that can efficiently be traversed forwards and backwards. The Linux kernel’s doubly-linked list is circular in nature. This means that to get from the head node to the tail, we can just travel one edge backwards. Similarly, to get from the tail node to the head, we can simply travel forwards “beyond” the tail and arrive back at the head. Declaring a node¶A node in a doubly-linked list is declared by adding a struct clown { unsigned long long shoe_size; const char *name; struct list_head node; /* the aforementioned member */ }; This may be an unfamiliar approach to some, as the classical explanation of a
linked list is a list node data structure with pointers to the previous and next
list node, as well the payload data. Linux chooses this approach because it
allows for generic list modification code regardless of what data structure is
contained within the list. Since the Declaring and initializing a list¶A doubly-linked list can then be declared as just another struct clown_car { int tyre_pressure[4]; struct list_head clowns; /* Looks like a node! */ }; /* ... Somewhere later in our driver ... */ static int circus_init(struct circus_priv *circus) { struct clown_car other_car = { .tyre_pressure = {10, 12, 11, 9}, .clowns = LIST_HEAD_INIT(other_car.clowns) }; INIT_LIST_HEAD(&circus->car.clowns); return 0; } A further point of confusion to some may be that the list itself doesn’t really
have its own type. The concept of the entire linked list and a
Adding nodes to the list¶Adding a node to the linked list is done through the We’ll return to our clown car example to illustrate how nodes get added to the list: static int circus_fill_car(struct circus_priv *circus) { struct clown_car *car = &circus->car; struct clown *grock; struct clown *dimitri; /* State 1 */ grock = kzalloc_obj(*grock); if (!grock) return -ENOMEM; grock->name = "Grock"; grock->shoe_size = 1000; /* Note that we're adding the "node" member */ list_add(&grock->node, &car->clowns); /* State 2 */ dimitri = kzalloc_obj(*dimitri); if (!dimitri) return -ENOMEM; dimitri->name = "Dimitri"; dimitri->shoe_size = 50; list_add(&dimitri->node, &car->clowns); /* State 3 */ return 0; } In State 1, our list of clowns is still empty: .------.
v |
.--------. |
| clowns |--'
'--------'
This diagram shows the singular “clowns” node pointing at itself. In this diagram, and all following diagrams, only the forward edges are shown, to aid in clarity. In State 2, we’ve added Grock after the list head: .--------------------.
v |
.--------. .-------. |
| clowns |---->| Grock |--'
'--------' '-------'
This diagram shows the “clowns” node pointing at a new node labeled “Grock”. The Grock node is pointing back at the “clowns” node. In State 3, we’ve added Dimitri after the list head, resulting in the following: .------------------------------------.
v |
.--------. .---------. .-------. |
| clowns |---->| Dimitri |---->| Grock |--'
'--------' '---------' '-------'
This diagram shows the “clowns” node pointing at a new node labeled “Dimitri”, which then points at the node labeled “Grock”. The “Grock” node still points back at the “clowns” node. If we wanted to have Dimitri inserted at the end of the list instead, we’d use
static int circus_fill_car(struct circus_priv *circus) { /* ... */ list_add_tail(&dimitri->node, &car->clowns); /* State 3b */ return 0; } This results in the following list: .------------------------------------.
v |
.--------. .-------. .---------. |
| clowns |---->| Grock |---->| Dimitri |--'
'--------' '-------' '---------'
This diagram shows the “clowns” node pointing at the node labeled “Grock”, which points at the new node labeled “Dimitri”. The node labeled “Dimitri” points back at the “clowns” node. Traversing the list¶To iterate the list, we can loop through all nodes within the list with
In our clown example, this results in the following somewhat awkward code: static unsigned long long circus_get_max_shoe_size(struct circus_priv *circus) { unsigned long long res = 0; struct clown *e; struct list_head *cur; list_for_each(cur, &circus->car.clowns) { e = list_entry(cur, struct clown, node); if (e->shoe_size > res) res = e->shoe_size; } return res; } The Note how the additional Using it would change our code to something like this: static unsigned long long circus_get_max_shoe_size(struct circus_priv *circus) { unsigned long long res = 0; struct clown *e; list_for_each_entry(e, &circus->car.clowns, node) { if (e->shoe_size > res) res = e->shoe_size; } return res; } This eliminates the need for the Removing nodes from the list¶The We can extend our previous example to remove one of the entries: static int circus_fill_car(struct circus_priv *circus) { /* ... */ list_add(&dimitri->node, &car->clowns); /* State 3 */ list_del(&dimitri->node); /* State 4 */ return 0; } The result of this would be this: .--------------------.
v |
.--------. .-------. | .---------.
| clowns |---->| Grock |--' | Dimitri |
'--------' '-------' '---------'
This diagram shows the “clowns” node pointing at the node labeled “Grock”, which points back at the “clowns” node. Off to the side is a lone node labeled “Dimitri”, which has no arrows pointing anywhere. Note how the Dimitri node does not point to itself; its pointers are intentionally set to a “poison” value that the list code refuses to traverse. If we wanted to reinitialize the removed node instead to make it point at itself
again like an empty list head, we can use static int circus_fill_car(struct circus_priv *circus) { /* ... */ list_add(&dimitri->node, &car->clowns); /* State 3 */ list_del_init(&dimitri->node); /* State 4b */ return 0; } This results in the deleted node pointing to itself again: .--------------------. .-------.
v | v |
.--------. .-------. | .---------. |
| clowns |---->| Grock |--' | Dimitri |--'
'--------' '-------' '---------'
This diagram shows the “clowns” node pointing at the node labeled “Grock”, which points back at the “clowns” node. Off to the side is a lone node labeled “Dimitri”, which points to itself. Traversing whilst removing nodes¶Deleting entries while we’re traversing the list will cause problems if we use
There is a solution to this however: An example of how to use it: static void circus_eject_insufficient_clowns(struct circus_priv *circus) { struct clown *e; struct clown *n; /* temporary storage for safe iteration */ list_for_each_entry_safe(e, n, &circus->car.clowns, node) { if (e->shoe_size < 500) list_del(&e->node); } } Proper memory management (i.e. freeing the deleted node while making sure nothing still references it) in this case is left as an exercise to the reader. Cutting a list¶There are two helper functions to cut lists with. Both take elements from the
list The first such function is In this example, it’s assumed we start with the following list: .----------------------------------------------------------------.
v |
.--------. .-------. .---------. .-----. .---------. |
| clowns |---->| Grock |---->| Dimitri |---->| Pic |---->| Alfredo |--'
'--------' '-------' '---------' '-----' '---------'
With the following code, every clown up to and including “Pic” is moved from
the “clowns” list head to a separate static void circus_retire_clowns(struct circus_priv *circus) { struct list_head retirement = LIST_HEAD_INIT(retirement); struct clown *grock, *dimitri, *pic, *alfredo; struct clown_car *car = &circus->car; /* ... clown initialization, list adding ... */ list_cut_position(&retirement, &car->clowns, &pic->node); /* State 1 */ } The resulting .----------------------.
v |
.--------. .---------. |
| clowns |---->| Alfredo |--'
'--------' '---------'
Meanwhile, the .--------------------------------------------------.
v |
.------------. .-------. .---------. .-----. |
| retirement |---->| Grock |---->| Dimitri |---->| Pic |--'
'------------' '-------' '---------' '-----'
The second function, static void circus_retire_clowns(struct circus_priv *circus) { struct list_head retirement = LIST_HEAD_INIT(retirement); struct clown *grock, *dimitri, *pic, *alfredo; struct clown_car *car = &circus->car; /* ... clown initialization, list adding ... */ list_cut_before(&retirement, &car->clowns, &pic->node); /* State 1b */ } The resulting .----------------------------------.
v |
.--------. .-----. .---------. |
| clowns |---->| Pic |---->| Alfredo |--'
'--------' '-----' '---------'
Meanwhile, the .--------------------------------------.
v |
.------------. .-------. .---------. |
| retirement |---->| Grock |---->| Dimitri |--'
'------------' '-------' '---------'
It should be noted that both functions will destroy links to any existing nodes
in the destination Moving entries and partial lists¶The In the following example, we’ll assume we start with two lists (“clowns” and “sidewalk”) in the following initial state “State 0”: .----------------------------------------------------------------.
v |
.--------. .-------. .---------. .-----. .---------. |
| clowns |---->| Grock |---->| Dimitri |---->| Pic |---->| Alfredo |--'
'--------' '-------' '---------' '-----' '---------'
.-------------------.
v |
.----------. .-----. |
| sidewalk |---->| Pio |--'
'----------' '-----'
We apply the following example code to the two lists: static void circus_clowns_exit_car(struct circus_priv *circus) { struct list_head sidewalk = LIST_HEAD_INIT(sidewalk); struct clown *grock, *dimitri, *pic, *alfredo, *pio; struct clown_car *car = &circus->car; /* ... clown initialization, list adding ... */ /* State 0 */ list_move(&pic->node, &sidewalk); /* State 1 */ list_move_tail(&dimitri->node, &sidewalk); /* State 2 */ } In State 1, we arrive at the following situation: .-----------------------------------------------------.
| |
v |
.--------. .-------. .---------. .---------. |
| clowns |---->| Grock |---->| Dimitri |---->| Alfredo |--'
'--------' '-------' '---------' '---------'
.-------------------------------.
v |
.----------. .-----. .-----. |
| sidewalk |---->| Pic |---->| Pio |--'
'----------' '-----' '-----'
In State 2, after we’ve moved Dimitri to the tail of sidewalk, the situation changes as follows: .-------------------------------------.
| |
v |
.--------. .-------. .---------. |
| clowns |---->| Grock |---->| Alfredo |--'
'--------' '-------' '---------'
.-----------------------------------------------.
v |
.----------. .-----. .-----. .---------. |
| sidewalk |---->| Pic |---->| Pio |---->| Dimitri |--'
'----------' '-----' '-----' '---------'
As long as the source and destination list head are part of the same list, we
can also efficiently bulk move a segment of the list to the tail end of the
list. We continue the previous example by adding a static void circus_clowns_exit_car(struct circus_priv *circus) { struct list_head sidewalk = LIST_HEAD_INIT(sidewalk); struct clown *grock, *dimitri, *pic, *alfredo, *pio; struct clown_car *car = &circus->car; /* ... clown initialization, list adding ... */ /* State 0 */ list_move(&pic->node, &sidewalk); /* State 1 */ list_move_tail(&dimitri->node, &sidewalk); /* State 2 */ list_bulk_move_tail(&sidewalk, &pic->node, &pio->node); /* State 3 */ } For the sake of brevity, only the altered “sidewalk” list at State 3 is depicted in the following diagram: .-----------------------------------------------.
v |
.----------. .---------. .-----. .-----. |
| sidewalk |---->| Dimitri |---->| Pic |---->| Pio |--'
'----------' '---------' '-----' '-----'
Do note that Rotating entries¶A common write operation on lists, especially when using them as queues, is to rotate it. A list rotation means entries at the front are sent to the back. For rotation, Linux provides us with two functions: The latter, In the following example, our starting state, State 0, is the following: .-----------------------------------------------------------------.
v |
.--------. .-------. .---------. .-----. .---------. .-----. |
| clowns |-->| Grock |-->| Dimitri |-->| Pic |-->| Alfredo |-->| Pio |-'
'--------' '-------' '---------' '-----' '---------' '-----'
The example code being used to demonstrate list rotations is the following: static void circus_clowns_rotate(struct circus_priv *circus) { struct clown *grock, *dimitri, *pic, *alfredo, *pio; struct clown_car *car = &circus->car; /* ... clown initialization, list adding ... */ /* State 0 */ list_rotate_left(&car->clowns); /* State 1 */ list_rotate_to_front(&alfredo->node, &car->clowns); /* State 2 */ } In State 1, we arrive at the following situation: .-----------------------------------------------------------------.
v |
.--------. .---------. .-----. .---------. .-----. .-------. |
| clowns |-->| Dimitri |-->| Pic |-->| Alfredo |-->| Pio |-->| Grock |-'
'--------' '---------' '-----' '---------' '-----' '-------'
Next, after the .-----------------------------------------------------------------.
v |
.--------. .---------. .-----. .-------. .---------. .-----. |
| clowns |-->| Alfredo |-->| Pio |-->| Grock |-->| Dimitri |-->| Pic |-'
'--------' '---------' '-----' '-------' '---------' '-----'
As is hopefully evident from the diagrams, the entries in front of “Alfredo” were cycled to the tail end of the list. Swapping entries¶Another common operation is that two entries need to be swapped with each other. For this, Linux provides us with In the following example, we have a list with three entries, and swap two of them. This is our starting state in “State 0”: .-----------------------------------------.
v |
.--------. .-------. .---------. .-----. |
| clowns |-->| Grock |-->| Dimitri |-->| Pic |-'
'--------' '-------' '---------' '-----'
static void circus_clowns_swap(struct circus_priv *circus) { struct clown *grock, *dimitri, *pic; struct clown_car *car = &circus->car; /* ... clown initialization, list adding ... */ /* State 0 */ list_swap(&dimitri->node, &pic->node); /* State 1 */ } The resulting list at State 1 is the following: .-----------------------------------------.
v |
.--------. .-------. .-----. .---------. |
| clowns |-->| Grock |-->| Pic |-->| Dimitri |-'
'--------' '-------' '-----' '---------'
As is evident by comparing the diagrams, the “Pic” and “Dimitri” nodes have traded places. Splicing two lists together¶Say we have two lists, in the following example one represented by a list head we call “knie” and one we call “stey”. In a hypothetical circus acquisition, the two list of clowns should be spliced together. The following is our situation in “State 0”: .-----------------------------------------.
| |
v |
.------. .-------. .---------. .-----. |
| knie |-->| Grock |-->| Dimitri |-->| Pic |--'
'------' '-------' '---------' '-----'
.-----------------------------.
v |
.------. .---------. .-----. |
| stey |-->| Alfredo |-->| Pio |--'
'------' '---------' '-----'
The function to splice these two lists together is static void circus_clowns_splice(void) { struct clown *grock, *dimitri, *pic, *alfredo, *pio; struct list_head knie = LIST_HEAD_INIT(knie); struct list_head stey = LIST_HEAD_INIT(stey); /* ... Clown allocation and initialization here ... */ list_add_tail(&grock->node, &knie); list_add_tail(&dimitri->node, &knie); list_add_tail(&pic->node, &knie); list_add_tail(&alfredo->node, &stey); list_add_tail(&pio->node, &stey); /* State 0 */ list_splice(&stey, &dimitri->node); /* State 1 */ } The .-----------------------------------------------------------------.
| |
v |
.------. .-------. .---------. .---------. .-----. .-----. |
| knie |-->| Grock |-->| Dimitri |-->| Alfredo |-->| Pio |-->| Pic |--'
'------' '-------' '---------' '---------' '-----' '-----'
^
.-------------------------------'
|
.------. |
| stey |--'
'------'
Traversing the This is because If we want to avoid this situation, Concurrency considerations¶Concurrent access and modification of a list needs to be protected with a lock in most cases. Alternatively and preferably, one may use the RCU primitives for lists in read-mostly use-cases, where read accesses to the list are common but modifications to the list less so. See Using RCU to Protect Read-Mostly Linked Lists for more details. Further reading¶Full List API¶
Parameters
Parameters
Parameters
Parameters
Description Initializes the list_head to point to itself. If it is a list header, the result is an empty list.
Parameters
Description Insert a new entry after the specified head. This is good for implementing stacks. Must be inlined to ensure it can be safely called with initdata arguments.
Parameters
Description Insert a new entry before the specified head. This is useful for implementing queues.
Parameters
Description Insert a new entry before the specified head, using a release barrier to set the ->next pointer that points to it. This is useful for implementing queues, in particular one that the elements will be walked through forwards locklessly.
Parameters
Note
Parameters
Description If old was empty, it will be overwritten.
Parameters
Description If old was empty, it will be overwritten.
Parameters
Parameters
Parameters
Parameters
Parameters
Description Move all entries between first and including last before head. All three entries must belong to the same linked list.
Parameters
Parameters
Parameters
Parameters
Parameters
Description This is the same as Any memory operations done before a
Parameters
Description tests whether a list is empty _and_ checks that no other CPU might be in the process of modifying either member (next or prev) NOTE using
Parameters
Parameters
Description Rotates list so that list becomes the new front of the list.
Parameters
Parameters
Description This helper moves the initial part of head, up to and including entry, from head to list. You should pass on entry an element you know is on head. list should be an empty list or a list you do not care about losing its data.
Parameters
Description This helper moves the initial part of head, up to but excluding entry, from head to list. You should pass in entry an element you know is on head. list should be an empty list or a list you do not care about losing its data. If entry == head, all entries on head are moved to list.
Parameters
Parameters
Parameters
Description The list at list is reinitialised
Parameters
Description Each of the lists is a queue. The list at list is reinitialised
Parameters
Parameters
Description Note, that list is expected to be not empty.
Parameters
Description Note, that list is expected to be not empty.
Parameters
Description Note that if the list is empty, it returns NULL.
Parameters
Description Note that if the list is empty, it returns NULL.
Parameters
Description Note that if the list is empty, it returns NULL.
Parameters
Parameters
Description Wraparound if pos is the last element (return the first element). Note, that list is expected to be not empty.
Parameters
Parameters
Description Wraparound if pos is the first element (return the last element). Note, that list is expected to be not empty.
Parameters
Parameters
Description Continue to iterate over a list, continuing after the current position.
Parameters
Parameters
Parameters
Parameters
Parameters
Parameters
Parameters
Parameters
Description Prepares a pos entry for use as a start point in
Parameters
Description Continue to iterate over list of given type, continuing after the current position.
Parameters
Description Start to iterate over list of given type backwards, continuing after the current position.
Parameters
Description Iterate over list of given type, continuing from current position.
Parameters
Description Iterate backwards over list of given type, continuing from current position.
Parameters
Parameters
Description Iterate over list of given type, continuing after current point, safe against removal of list entry.
Parameters
Description Iterate over list of given type from current point, safe against removal of list entry.
Parameters
Description Iterate backwards over list of given type, safe against removal of list entry.
Parameters
Description list_safe_reset_next is not safe to use in general if the list may be modified concurrently (eg. the lock is dropped in the loop body). An exception to this is if the cursor element (pos) is pinned in the list, and list_safe_reset_next is called after re-taking the lock and before completing the current iteration of the loop body.
Parameters
Description Not that not all removal functions will leave a node in unhashed
state. For example,
Parameters
Description This variant of
Parameters
Parameters
Description Note that this function leaves the node in hashed state. Use
Parameters
Description Note that this function leaves the node in unhashed state.
Parameters
Description Insert a new entry after the specified head. This is good for implementing stacks.
Parameters
Parameters
Parameters
Description This makes n appear to be its own predecessor on a headless hlist.
The point of this is to allow things like
Parameters
Parameters
Description Check whether the node is the only node of the head without accessing head, thus avoiding unnecessary cache misses.
Parameters
Description Move a list from one list head to another. Fixup the pprev reference of the first entry if it exists.
Parameters
Description to can be empty, from must contain at least last.
Parameters
Parameters
Parameters
Parameters
Parameters
Private List API¶Provides a set of list primitives identical in function to those in
Parameters
Parameters
Parameters
Parameters
Parameters
Description Wraparound if pos is the last element (return the first element). Note, that list is expected to be not empty.
Parameters
Parameters
Description Wraparound if pos is the first element (return the last element). Note, that list is expected to be not empty.
Parameters
Parameters
Parameters
Parameters
Description Continue to iterate over list of given type, continuing after the current position.
Parameters
Description Start to iterate over list of given type backwards, continuing after the current position.
Parameters
Description Iterate over list of given type, continuing from current position.
Parameters
Description Iterate backwards over list of given type, continuing from current position.
Parameters
Parameters
Description Iterate over list of given type, continuing after current point, safe against removal of list entry.
Parameters
Description Iterate over list of given type from current point, safe against removal of list entry.
Parameters
Description Iterate backwards over list of given type, safe against removal of list entry.
Parameters
Description list_safe_reset_next is not safe to use in general if the list may be modified concurrently (eg. the lock is dropped in the loop body). An exception to this is if the cursor element (pos) is pinned in the list, and list_safe_reset_next is called after re-taking the lock and before completing the current iteration of the loop body. |
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |