요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: GPL-2.0+
=====================
Linked Lists in Linux
=====================
:Author: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
.. contents::
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
``<linux/list.h>``.
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 list_head
member to the data structure you wish to be contained in the list:
.. code-block:: c
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 struct list_head member is not a pointer
but part of the data structure proper, the container_of() pattern can be used by
the list implementation to access the payload data regardless of its type, while
staying oblivious to what said type actually is.
Declaring and initializing a list
---------------------------------
A doubly-linked list can then be declared as just another struct list_head,
and initialized with the LIST_HEAD_INIT() macro during initial assignment, or
with the INIT_LIST_HEAD() function later:
.. code-block:: c
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
struct list_head member that points to other entries in the list are one and
the same.
Adding nodes to the list
------------------------
Adding a node to the linked list is done through the list_add() macro.
We'll return to our clown car example to illustrate how nodes get added to the
list:
.. code-block:: c
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(sizeof(*grock), GFP_KERNEL);
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(sizeof(*dimitri), GFP_KERNEL);
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
list_add_tail(). Our code would then look like this:
.. code-block:: c
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
list_for_each().
In our clown example, this results in the following somewhat awkward code:
.. code-block:: c
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 list_entry() macro internally uses the aforementioned container_of() to
retrieve the data structure instance that ``node`` is a member of.
Note how the additional list_entry() call is a little awkward here. It's only
there because we're iterating through the ``node`` members, but we really want
to iterate through the payload, i.e. the ``struct clown`` that contains each
node's struct list_head. For this reason, there is a second macro:
list_for_each_entry()
Using it would change our code to something like this:
.. code-block:: c
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 list_entry() step, and our loop cursor is now
of the type of our payload. The macro is given the member name that corresponds
to the list's struct list_head within the clown data structure so that it can
still walk the list.
Removing nodes from the list
----------------------------
The list_del() function can be used to remove entries from the list. It not only
removes the given entry from the list, but poisons the entry's ``prev`` and
``next`` pointers, so that unintended use of the entry after removal does not
go unnoticed.
We can extend our previous example to remove one of the entries:
.. code-block:: c
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 list_del_init() instead:
.. code-block:: c
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
list_for_each() and list_for_each_entry(), as deleting the current entry would
modify the ``next`` pointer of it, which means the traversal can't properly
advance to the next list entry.
There is a solution to this however: list_for_each_safe() and
list_for_each_entry_safe(). These take an additional parameter of a pointer to
a struct list_head to use as temporary storage for the next entry during
iteration, solving the issue.
An example of how to use it:
.. code-block:: c
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 ``head``, and replace the contents of the list ``list``.
The first such function is list_cut_position(). It removes all list entries from
``head`` up to and including ``entry``, placing them in ``list`` instead.
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 struct list_head initialized at local
stack variable ``retirement``:
.. code-block:: c
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 ``car->clowns`` list would be this::
.----------------------.
v |
.--------. .---------. |
| clowns |---->| Alfredo |--'
'--------' '---------'
Meanwhile, the ``retirement`` list is transformed to the following::
.--------------------------------------------------.
v |
.------------. .-------. .---------. .-----. |
| retirement |---->| Grock |---->| Dimitri |---->| Pic |--'
'------------' '-------' '---------' '-----'
The second function, list_cut_before(), is much the same, except it cuts before
the ``entry`` node, i.e. it removes all list entries from ``head`` up to but
excluding ``entry``, placing them in ``list`` instead. This example assumes the
same initial starting list as the previous example:
.. code-block:: c
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 ``car->clowns`` list would be this::
.----------------------------------.
v |
.--------. .-----. .---------. |
| clowns |---->| Pic |---->| Alfredo |--'
'--------' '-----' '---------'
Meanwhile, the ``retirement`` list is transformed to the following::
.--------------------------------------.
v |
.------------. .-------. .---------. |
| retirement |---->| Grock |---->| Dimitri |--'
'------------' '-------' '---------'
It should be noted that both functions will destroy links to any existing nodes
in the destination ``struct list_head *list``.
Moving entries and partial lists
--------------------------------
The list_move() and list_move_tail() functions can be used to move an entry
from one list to another, to either the start or end respectively.
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:
.. code-block:: c
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 list_bulk_move_tail() after
State 2, moving Pic and Pio to the tail end of the sidewalk list.
.. code-block:: c
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 list_bulk_move_tail() does not do any checking as to whether all
three supplied ``struct list_head *`` parameters really do belong to the same
list. If you use it outside the constraints the documentation gives, then the
result is a matter between you and the implementation.
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: list_rotate_left() and
list_rotate_to_front(). The former can be pictured like a bicycle chain, taking
the entry after the supplied ``struct list_head *`` and moving it to the tail,
which in essence means the entire list, due to its circular nature, rotates by
one position.
The latter, list_rotate_to_front(), takes the same concept one step further:
instead of advancing the list by one entry, it advances it *until* the specified
entry is the new front.
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:
.. code-block:: c
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 list_rotate_to_front() call, we arrive in the following
State 2::
.-----------------------------------------------------------------.
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 list_swap().
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 |-'
'--------' '-------' '---------' '-----'
.. code-block:: c
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 list_splice(). Our example
code is as follows:
.. code-block:: c
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 list_splice() call here adds all the entries in ``stey`` to the list
``dimitri``'s ``node`` list_head is in, after the ``node`` of ``dimitri``. A
somewhat surprising diagram of the resulting "State 1" follows::
.-----------------------------------------------------------------.
| |
v |
.------. .-------. .---------. .---------. .-----. .-----. |
| knie |-->| Grock |-->| Dimitri |-->| Alfredo |-->| Pio |-->| Pic |--'
'------' '-------' '---------' '---------' '-----' '-----'
^
.-------------------------------'
|
.------. |
| stey |--'
'------'
Traversing the ``stey`` list no longer results in correct behavior. A call of
list_for_each() on ``stey`` results in an infinite loop, as it never returns
back to the ``stey`` list head.
This is because list_splice() did not reinitialize the list_head it took
entries from, leaving its pointer pointing into what is now a different list.
If we want to avoid this situation, list_splice_init() can be used. It does the
same thing as list_splice(), except reinitalizes the donor list_head after the
transplant.
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 Documentation/RCU/listRCU.rst for more
details.
Further reading
---------------
* `How does the kernel implements Linked Lists? - KernelNewbies <https://kernelnewbies.org/FAQ/LinkedLists>`_
Full List API
=============
.. kernel-doc:: include/linux/list.h
:internal:
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
Linux linked list 소개
1-26SPDX 라이선스 식별자: GPL-2.0+
Linux의 linked list (Linked Lists in Linux)
저자: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
이 페이지에는 contents directive가 있습니다.
Linked list는 많은 program에서 사용하는 가장 기본적인 data structure 중 하나입니다. Linux kernel은 여러 종류의 linked list를 구현합니다. 이 문서는 linked list 일반 이론이 아니라 새 kernel developer가 Linux kernel 구현을 사용하는 방법을 설명합니다.
Linked list가 널리 쓰이기는 하지만 단순한 array로 충분하지 않은 경우에도 최선의 data structure인 때는 드뭅니다. 특히 data locality가 좋지 않으므로 performance가 중요할 때는 나쁜 선택입니다. Concurrent access가 관련된 경우를 포함해 kernel의 다른 generic data structure도 익히기를 적극 권장합니다.
Linux doubly linked list
27-40Linux의 doubly linked list 구현
Linux linked list 구현은 `<linux/list.h>` header file을 include하여 사용할 수 있습니다.
Doubly-linked list는 앞뒤 방향으로 효율적으로 순회할 수 있는 익숙한 형태입니다. Linux kernel의 doubly-linked list는 circular 구조이므로 head node에서 edge 하나를 뒤로 이동하면 tail에 닿고, tail에서 앞으로 더 이동하면 head로 돌아옵니다.
node 선언
41-63Doubly-linked list의 node는 list에 포함할 data structure에 `struct list_head` member를 추가하여 선언합니다.
.. code-block:: c
struct clown {
unsigned long long shoe_size;
const char *name;
struct list_head node; /* the aforementioned member */
};
전통적인 설명은 previous/next node pointer와 payload를 한 list node structure에 두지만 Linux는 payload structure 안에 `struct list_head`를 embedded합니다. 이 방식은 list에 담긴 data type과 무관하게 generic modification code를 사용할 수 있게 합니다.
`struct list_head` member는 pointer가 아니라 structure 자체의 일부이므로 list 구현은 `container_of()` pattern으로 실제 type을 몰라도 payload data에 접근할 수 있습니다.
list 선언과 초기화
64-96Doubly-linked list 자체도 `struct list_head`로 선언합니다. 최초 assignment에서는 `LIST_HEAD_INIT()` macro로, 이후에는 `INIT_LIST_HEAD()` function으로 초기화합니다.
.. code-block:: c
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;
}
List 자체에 별도의 type이 없다는 점이 혼동될 수 있습니다. 전체 linked list라는 개념과 다른 entry를 가리키는 `struct list_head` member는 같은 표현을 사용합니다.
head 뒤에 node 추가
97-173Linked list에 node를 추가할 때는 `list_add()` macro를 사용합니다. 다음 clown car 예제는 Grock과 Dimitri를 차례로 list head 뒤에 넣습니다.
.. code-block:: c
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(sizeof(*grock), GFP_KERNEL);
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(sizeof(*dimitri), GFP_KERNEL);
if (!dimitri)
return -ENOMEM;
dimitri->name = "Dimitri";
dimitri->shoe_size = 50;
list_add(&dimitri->node, &car->clowns);
/* State 3 */
return 0;
}
State 1에서는 clown list가 비어 있고 head가 자신을 가리킵니다.
원문의 forward edge만 표시한 그림을 구조화했습니다.
State 2에서는 list head 바로 뒤에 Grock을 추가했습니다. Grock의 다음 edge는 다시 `clowns` head로 돌아갑니다.
새 node가 head 다음 위치에 들어갑니다.
State 3에서는 Dimitri를 다시 head 뒤에 추가했으므로 순서는 `clowns`, Dimitri, Grock이 됩니다.
최근 `list_add()`로 넣은 Dimitri가 첫 entry가 됩니다.
tail에 node 추가
174-201Dimitri를 list 끝에 넣으려면 `list_add_tail()`을 사용합니다.
.. code-block:: c
static int circus_fill_car(struct circus_priv *circus)
{
/* ... */
list_add_tail(&dimitri->node, &car->clowns);
/* State 3b */
return 0;
}
그 결과 Grock 뒤에 Dimitri가 오고 Dimitri의 다음 edge가 `clowns` head로 돌아갑니다.
`list_add_tail()`은 새 entry를 마지막 위치에 삽입합니다.
list 순회
202-257`list_for_each()`로 list의 모든 node를 순회할 수 있습니다. 다만 cursor가 `struct list_head`이므로 payload를 얻기 위해 `list_entry()`를 호출해야 합니다.
.. code-block:: c
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;
}
`list_entry()`는 내부에서 `container_of()`를 사용해 `node`가 속한 data structure instance를 찾습니다. 실제로 순회하려는 것은 node가 아니라 이를 포함하는 `struct clown` payload이므로 이 추가 변환은 다소 번거롭습니다.
`list_for_each_entry()`를 사용하면 payload type의 cursor로 직접 순회할 수 있습니다.
.. code-block:: c
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;
}
이 macro에는 clown structure 안에서 list의 `struct list_head`에 해당하는 member name을 전달하므로 `list_entry()` 단계 없이도 list를 이동할 수 있습니다.
node 제거와 재초기화
258-331`list_del()`은 entry를 list에서 제거하고 `prev` 및 `next` pointer를 poison value로 바꿉니다. 따라서 제거한 entry를 의도치 않게 다시 사용하면 조용히 넘어가지 않습니다.
.. code-block:: c
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;
}
State 4에서 Grock만 list에 남고 Dimitri는 분리되어 어느 곳도 가리키지 않는 poisoned 상태입니다.
유효한 circular list와 분리된 poisoned node를 함께 나타냅니다.
제거한 node를 빈 list head처럼 다시 자신을 가리키게 하려면 `list_del_init()`을 사용합니다.
.. code-block:: c
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;
}
State 4b에서 Dimitri는 분리되어 있지만 self-loop로 재초기화되었습니다.
기존 list와 self-linked Dimitri가 각각 독립된 circular list입니다.
제거하면서 안전하게 순회
332-362`list_for_each()` 또는 `list_for_each_entry()`로 순회하면서 현재 entry를 삭제하면 `next` pointer가 바뀌어 다음 entry로 올바르게 이동할 수 없습니다.
`list_for_each_safe()`와 `list_for_each_entry_safe()`는 iteration 중 다음 entry를 임시 저장할 추가 `struct list_head` pointer를 받아 이 문제를 해결합니다.
.. code-block:: c
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);
}
}
삭제한 node를 free하면서 다른 reference가 없는지 확인하는 올바른 memory management는 이 예제의 범위를 벗어납니다.
list 자르기
363-453List를 자르는 helper는 두 가지이며 모두 `head` list에서 element를 떼어 `list` destination의 내용을 대체합니다.
`list_cut_position()`은 `head`부터 `entry`를 포함한 위치까지 제거해 `list`로 옮깁니다. 시작 상태는 다음과 같습니다.
Grock부터 Alfredo까지 네 entry가 있는 circular list입니다.
다음 code는 Pic까지의 모든 clown을 local stack의 `retirement` list로 옮깁니다.
.. code-block:: c
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 */
}
`car->clowns`에는 Alfredo만 남습니다.
`retirement`에는 Grock, Dimitri, Pic이 순서대로 들어갑니다.
`list_cut_before()`는 `entry` 직전까지만 잘라 `entry`는 원본에 남깁니다. 같은 초기 상태에서 다음 code를 적용합니다.
.. code-block:: c
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 */
}
Pic과 Alfredo가 `car->clowns`에 남습니다.
Grock과 Dimitri만 `retirement`로 이동합니다.
두 함수 모두 destination `struct list_head *list`에 기존 node가 연결되어 있었다면 그 link를 파괴한다는 점에 주의해야 합니다.
entry와 부분 list 이동
454-572`list_move()`와 `list_move_tail()`은 entry를 다른 list의 시작 또는 끝으로 각각 옮깁니다. 초기 State 0에는 `clowns`와 `sidewalk` 두 list가 있습니다.
Clowns에는 네 명, sidewalk에는 Pio가 있습니다.
.. code-block:: c
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 */
}
State 1에서 `list_move()`는 Pic을 sidewalk head 바로 뒤로 옮깁니다.
Pic이 sidewalk의 첫 entry가 됩니다.
State 2에서 `list_move_tail()`은 Dimitri를 sidewalk의 tail로 옮깁니다.
Clowns에는 Grock과 Alfredo, sidewalk에는 Pic, Pio, Dimitri가 남습니다.
Source와 destination list head가 같은 list에 속한다면 `list_bulk_move_tail()`로 연속 구간을 list tail에 효율적으로 옮길 수 있습니다. 다음 code는 Pic부터 Pio까지를 sidewalk tail로 이동합니다.
.. code-block:: c
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 */
}
Dimitri가 앞에 오고 Pic과 Pio 구간이 tail로 이동합니다.
`list_bulk_move_tail()`은 전달한 세 `struct list_head *`가 실제로 같은 list에 속하는지 검사하지 않습니다. 문서가 정한 제약을 벗어나 호출하면 결과를 보장할 수 없습니다.
entry 회전
573-639Queue처럼 list를 사용할 때 흔한 write operation은 앞의 entry를 뒤로 보내는 rotation입니다. Linux는 `list_rotate_left()`와 `list_rotate_to_front()`를 제공합니다.
`list_rotate_left()`는 circular bicycle chain처럼 head 다음 entry를 tail로 옮겨 전체 list를 한 칸 회전합니다. `list_rotate_to_front()`는 지정 entry가 새 front가 될 때까지 회전합니다.
초기 clown 순서입니다.
.. code-block:: c
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 */
}
Grock이 tail로 이동합니다.
Alfredo 앞의 entry가 tail 쪽으로 순환 이동합니다.
entry 교환
640-682두 entry의 위치를 바꾸는 흔한 작업에는 `list_swap()`을 사용합니다. State 0은 세 entry로 시작합니다.
Grock, Dimitri, Pic 순서입니다.
.. code-block:: c
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 */
}
State 1에서는 Pic과 Dimitri의 위치가 서로 바뀝니다.
Grock, Pic, Dimitri 순서입니다.
두 list 잇기
683-757두 circus list `knie`와 `stey`를 합치는 예제입니다. 초기 State 0은 다음과 같습니다.
두 개의 독립된 circular list입니다.
두 list를 잇는 함수는 `list_splice()`입니다.
.. code-block:: c
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 */
}
이 호출은 `stey`의 모든 entry를 Dimitri의 `node` 다음에 삽입합니다.
Knie에는 모든 entry가 연결되지만 stey head는 새 list 내부를 계속 가리켜 자신에게 돌아오지 못합니다.
이 상태에서 `stey`를 `list_for_each()`로 순회하면 head로 돌아오지 않아 infinite loop가 됩니다. `list_splice()`가 donor `list_head`를 재초기화하지 않아 pointer가 이제 다른 list 내부를 가리키기 때문입니다.
이를 피하려면 `list_splice_init()`을 사용합니다. `list_splice()`와 같은 transplant를 수행한 뒤 donor `list_head`를 다시 초기화합니다.
concurrency 고려 사항
758-766대부분의 경우 list에 대한 concurrent access와 modification은 lock으로 보호해야 합니다. Read가 많고 modification이 적은 use case에서는 list용 RCU primitive를 사용하는 편이 좋습니다. 자세한 내용은 `Documentation/RCU/listRCU.rst`를 참조하십시오.
더 읽을 자료
767-771더 읽을 자료 (Further reading)
전체 List API
772-776전체 List API (Full List API)
List API의 internal kernel-doc은 `include/linux/list.h`에서 가져옵니다.
.. kernel-doc:: include/linux/list.h
:internal:
요약과 해설
list.rst:1-776Linux list API는 payload structure 안에 `struct list_head`를 embedded하고 `container_of()`로 원래 object를 찾는 intrusive circular doubly linked list입니다. 이 방식은 payload type과 무관한 generic operation을 가능하게 하지만 cache locality가 나쁘므로 성능이 중요한 곳에서는 다른 structure도 검토해야 합니다.
`list_add()`와 `list_add_tail()`은 head 또는 tail에 삽입하고, `list_for_each_entry()`는 payload type으로 순회합니다. 삭제 중 순회에는 `_safe` variant를 사용하며 `list_del()` 뒤 node는 poisoned되고 `list_del_init()` 뒤에는 self-linked 상태로 재초기화됩니다.
절단, 이동, 회전, 교환과 splice helper는 pointer를 빠르게 재연결하지만 membership을 검증하지 않는 API도 있습니다. 특히 `list_splice()` 뒤 donor head는 유효한 독립 list가 아니므로 다시 사용할 때는 `list_splice_init()`을 선택해야 하며 concurrent access는 lock 또는 RCU로 보호해야 합니다.