カーネルリンクリストの簡単な使用

序文

(1) まずカーネルリンクリストを導入します。

1. カーネル リンク リストはシステム内に現れることが多く、Ubuntu によって内部的に提供される双方向の循環リンク リストです。

        二重リンク リスト: データ フィールド、前および次のポインターなど、すべてがカスタマイズされます。

         カーネル リンク リスト: データ フィールドのみがカスタマイズされます。他の操作はカーネル リンク リスト ファイル list.h で定義されます。

2. カーネルリンクリストと通常のリンクリストの違い:
        1. 通常のリンクリストではデータフィールドとポインタフィールドの区別がありません。データとポインタは全体を形成しますが、カーネル リンク リストのデータとポインタは完全に分離されており、直接的な関係はありません。
        2. 通常のリンクリストでは、すべてのノードのデータは同じタイプですが、カーネルリンクリストでは、各ノードのタイプが異なる場合があります。
        3. 通常のリンク リストの各ノードは、先行ポインタ/後続ポインタのペアを 1 つしか持たないため、1 つのリンク リストにのみ出現できますが、カーネル リンク リストは先行ポインタ/後続ポインタの複数のペアを持つことができます。

(2) カーネルリンクリストの利点:

        一般的な一方向リンク リストおよび二重リンク リスト ポインタは、リンク リスト ノードの開始位置を指します。Linux カーネルで実際に使用するには、データ領域を固定する必要があるなど、いくつかの制限があります。多様な。異なるデータ領域ごとにリンク リストのセットが必要となるため、この方法ではリンク リストのユニバーサル セットを構築できません。この目的を達成するために、Linux カーネルはすべてのリンク リスト操作メソッドの共通部分を抽出し、異なる部分はコード作成者自身に任せます。

        Linux カーネルは、純粋なリンク リスト カプセル化のセットを実装しています。リンク リスト ノード データ構造には、ポインタ領域のみがあり、データ領域はありません。また、ノード関数の作成、ノード関数の挿入、ノード関数の削除、ノード関数のトラバースなど、さまざまな操作関数もカプセル化されています。ノード機能など
 

(3) リンクリストの初期化

リンクリストの構成:大きな構造と小さな構造

大きな構造: データとポインターが含まれます (カーネルのリンク リスト内の小さな構造)
小さな構造: カーネルのリンクされたリストの前のノードと次のノードを指すために使用されるポインター
知らせ:
        大きな構造体のうち、小さな構造体は通常の変数でなければならず、ポインタは使用できません。
typedef struct kernel{//大结构体类型
        int data;//数据域
        struct list_head list;//小结构体名称 
}kernel_t,*pkernel_t;
list.h ファイル内の List_head は次のように導入されます。        
struct list_head{
	struct list_head *next,*prev;
};

(4) リンクリストの追加、削除、変更、移動

1.挿入操作

アイデア:

内核链表中插入数据的思路:
把用户传进来的两个参数: 新节点 + 头节点
演变成很三个参数: 新节点 + 新节点的前驱节点 + 新节点的后继节点

1 /**
2 * 节点头插
3 *
4 * 使用内联函数list_add(struct list_head *new, struct list_head *head)         
5 * 将会自动调用__list_add,实现功能:将节点 new 插入到 head 后面;实现头插功能
6 */
7 static inline void list_add(struct list_head *xnew, struct list_head *head)
8 {
9         __list_add(new, head, head->next);
10 }

ヘッド挿入方法:

//头插操作 
void kernel_insert_head(pkernel_t p,int d)
{
    //根据数据域创建节点,并头插
    pkernel_t node=(pkernel_t)malloc(sizeof(kernel_t));
    if(NULL==node){
        perror("malloc");//当创建失败,根据错误码,显示原因
        return;//
    }    
    //根据传入的参,对节点数据域赋值 
    node->data=d;
    
    //将node插在p的后面 
    list_add(&node->list,&p->list);
     
}

list_add(&node->list,&p->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

/**
 * 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.
 */
static inline void list_add(struct list_head *new, struct list_head *head)
{
	__list_add(new, head, head->next);
}
テール挿入方法:
void kernel_insert_tail(pkernel_t p,int d)
{
    //根据数据域创建节点,并尾插
    pkernel_t node=(pkernel_t)malloc(sizeof(kernel_t));
    if(NULL==node){
        return;
    }
    
    node->data=d;//根据数据域赋值 
    
    list_add_tail(&node->list,&p->list);      
}

list.h 内:

/**
 * 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);
}
2. 削除操作
//删除操作
void kernel_del(pkernel_t p,int d)
{
    //1>查找数据域d是否存在
    pkernel_t pos;  //定义的循环变量
    list_for_each_entry(pos,&p->list,list)
    {
        if(pos->data==d){
            list_del(&pos->list);
            free(pos);
            return;
        }
        
    }
    printf("没有找到该数据\n");   
}

for ループ関数:

/**
 * list_for_each_entry	-	iterate over list of given type
 * @pos:	the type * to use as a loop cursor.
 * @head:	the head for your list.
 * @member:	the name of the list_struct within the struct.
 */
#define list_for_each_entry(pos, head, member)				\
	for (pos = list_entry((head)->next, typeof(*pos), member);	\
	     &pos->member != (head); 	\
	     pos = list_entry(pos->member.next, typeof(*pos), member))

list_del 関数

/*
 * 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_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_POISON1;
	entry->prev = LIST_POISON2;
}
#else
extern void __list_del_entry(struct list_head *entry);
extern void list_del(struct list_head *entry);
#endif
3. アップデート操作
//替换操作
void kernel_update(pkernel_t p,int old,int new)
{
    //1>查找一下旧数据是否在链表当中
    pkernel_t pos;
    pkernel_t temp;//传输替换的数据结构体
    list_for_each_entry(pos,&p->list,list)
    {
        if(pos->data==old){
                temp=(pkernel_t)malloc(sizeof(kernel_t));
                temp->data=new;
                list_replace_init(&pos->list,&temp->list);
                free(pos); 
                return;
        }

    }
    printf("没有找到该数据\n");
}

   交換する:

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;
}

static inline void list_replace_init(struct list_head *old,
					struct list_head *new)
{
	list_replace(old, new);
	INIT_LIST_HEAD(old);
}
static inline void INIT_LIST_HEAD(struct list_head *list)
{
	list->next = list;
	list->prev = list;
}

(5) リンクリストの走査

カーネルのリンク リストにはデータ フィールドがないため、データ フィールドは独自の操作によって定義されます。そのため、小さな構造間の差分を通じて大きな構造のアドレスを取得する前に、まずデータ フィールドのサイズを知る必要があります。構造体とデータフィールド。
                              

以下のように分析します。

  宏: #define list_entry(ptr, type, member) \
                container_of(ptr, type, member)
                
            #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
            #define container_of(ptr, type, member) ({			\
                const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
                (type *)( (char *)__mptr - offsetof(type,member) );})
            解释: ptr:小结构体的指针类型:struct list_head *
                  type: 大结构体的类型:struct kernel ·
                  member:小结构体在大结构体中的名称:list
                  
            1>分析 
               list_entry(ptr, type, member)= container_of(ptr, type, member)
                
            2>container_of(ptr, type, member)=
                 const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
                (type *)( (char *)__mptr - offsetof(type,member) );}) 

            3>offsetof(TYPE, member) = ((size_t) &((TYPE *)0)->member)


            4>综上所述,我们调用:list_entry(ptr, type, member)就会获得下面的内容
                const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
                (type *)( (char *)__mptr - ((size_t) &((type *)0)->member)
                
            重点:宏分析--->是你掌握内核链表的重要一环
                1> const typeof( ((type *)0)->member ) *__mptr = (ptr);
                ((type *)0)--->将0强转为 大结构体的指针类型 0x0
                ((type *)0)->member 大结构体下的member= list 
                    typeof--->取类型
                  const struct list_head  * __mptr=ptr;
                    定义一个小结构体指针类型变量,保存小结构体的地址
                    
                2>(type *)( (char *)__mptr - ((size_t) &((type *)0)->member)
                    ((size_t) &((type *)0)->member)
                    (size_t) &list
                   -  无符号整型   ,将list的地址转化为无符号整型
                     
                   (type *)( (char *)__mptr 
                            字符类型 --->一次只能运算一个字符
                     (type *)大结构体类型  --->mptr转化为 char * 运算后,转化为大结构体类型
                        减去小结构体地址  就会得到 大结构体地址
   移动一个字节:偏移量:1                     
                总结: 
                     list_entry(ptr, type, member);
                              通过小结构体指针类型ptr  得到 大结构体指针类型
                              
                使用实例: 
                    struct list_head *ptr=随便一个小结构体地址;外部传入
                    pkernel_t node=list_entry(ptr,kernel,list);
                    printf("%d\n",node->data);

トラバーサル機能:

//遍历
void display(pkernel_t p)
{
	
	if( p->list.next == &p->list )
 {
	printf("该表为空!!\n");
	return -1 ;
 }
    printf("遍历结果为:");	
    pkernel_t node;
    struct list_head *pos;
    list_for_each(pos,&p->list){//循环 
        node=list_entry(pos,kernel_t,list);
        printf("%d ",node->data);                    
    }
    printf("\n"); 
}

おすすめ

転載: blog.csdn.net/apple_71040140/article/details/132175057