Linuxカーネルリンクリストは、Linuxカーネルの最も古典的なデータ構造の1つです。Linuxカーネルリンクリストの最大の利点は、メモリを節約し、リンクリストのさまざまな操作が高速で、アイデアの実現が更新され、メモリ管理などのLinuxカーネルで頻繁に使用されることです。 、プロセススケジューリングなど。
Linuxカーネルのリンクリストは、双方向の循環リンクリストです。コアアイデアは、リンクリストノードをデータノードに配置し、フロントポインタとバックポインタを介してフロントデータノードとバックデータノードのリンクリストノードをポイントし、データノードを相互に接続できるようにすることです。
1.リンクリストノードの定義
struct list_head {
struct list_head *next, *prev;
};
次に、カーネルリンクリストの初期化
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
INIT_LIST_HEAD関数の前のポインターと次のポインターは、リンクされたリストのヘッドノードを指します。LIST_HEAD_INITマクロは、リンクされたリストのヘッドノードの次のポインターを初期化し、prevポインターは、ヘッドノードのアドレスを保存します。実際、効果は、INI_LIST_HEAD関数の初期化効果と同じです。LIST_HEADマクロは、リンクされたリストノードの完全な初期化です。例として実際のコードを見てみましょう。
struct list_head list;
LIST_HEAD(list); //list = {&list, &list}
取得するには、C / C ++ Linuxサーバーアーキテクトの学習資料とqun(563998835)が必要です(C / C ++、Linux、golangテクノロジー、Nginx、ZeroMQ、MySQL、Redis、fastdfs、MongoDB、ZK、ストリーミングメディア、CDN、P2P、K8Sを含むデータ) 、Docker、TCP / IP、coroutine、DPDK、ffmpegなど)、無料共有
3つ目は、カーネルリンクリストがノードに挿入されることです。
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
#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
/**
* list_add - add a new entry
* @new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
#ifndef CONFIG_DEBUG_LIST
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
#else
extern void list_add(struct list_head *new, struct list_head *head);
#endif
/**
* list_add_tail - add a new entry
* @new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
描画して説明することで、カーネルリンクリストを挿入する操作を理解しやすくなります。
コードを直接見ると、カーネルリンクリストは実際にはテール補間法を使用しており、ヘッドノードがあることがわかります。
ノードを挿入する前のカーネルリンクリストは、次の図のようになります。
node.PNGを挿入する前
次の図に示すように、カーネルリンクリストがノードに挿入されます。
node.PNGを挿入した後
list_add_tail関数の分析と組み合わせると、リンクリストリストがノードノードに挿入された場合、next = list-> next、prev = list;次に、__ list_addを呼び出して新しいノードをリンクリストに挿入することがわかります。
第四に、カーネルリンクリストのノードを削除します
/*
* Delete a list entry by making the prev/next entries
* point to each other.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
/**
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
* Note: list_empty() on entry does not return true after this, the entry is
* in an undefined state.
*/
#ifndef CONFIG_DEBUG_LIST
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
#else
extern void list_del(struct list_head *entry);
#endif
コードを直接分析すると、ノードを削除すると、実際にはターゲットノードの後ろにある次のノードのフォワードポインタがターゲットノードのフォワードノードを指し、ターゲットノードのバックワードノードのフォワードポインタがターゲットノードを指していることがわかります。フォワードノード。
Linuxカーネルのリンクリストの最後には、トラバース、データノード構造の最初のアドレスの取得など、多くの興味深い実装があります。これらについては、次の章で詳しく分析します。