Linuxカーネルリンクリストの詳細な分析

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カーネルのリンクリストの最後には、トラバース、データノード構造の最初のアドレスの取得など、多くの興味深い実装があります。これらについては、次の章で詳しく分析します。

おすすめ

転載: blog.csdn.net/qq_40989769/article/details/107865671