Linux 内核双向链表

0x01:链表

链表是Linux内核中非常重要的数据结构,在日常内核驱动开发过程中,往往需要遍历内核的某个链表,内核提供了一套完整的双链表机制,使开发者可以在内核私有数据结构中轻松构建双向链表,一起学习双向链表的构建和遍历方法,具体链表的知识可以参考文末链接;

0x02:数据结构及关键函数

  • 链表可以用list_head来描述

    struct list_head {
    struct list_head *next, *prev;
    };
    

    list_head结构体不包含链表节点的数据区,而是通常嵌入其他数据结构,如page 数据结构中就嵌入了lru链表节点,通常的做法是将page结构体挂入LRU链表。

    struct page {
    ...
    struct list_head lru;
    ...
    }
    
  • 链表头初始化

    • 静态初始化,name为链表头名
    #define LIST_HEAD_INIT(name) { &(name), &(name) }
    
    #define LIST_HEAD(name) \
    struct list_head name = LIST_HEAD_INIT(name)
    
    • 动态初始化
     * INIT_LIST_HEAD - Initialize a list_head structure
     * @list: list_head structure to be initialized.
     *
     * Initializes the list_head to point to itself.  If it is a list header,
     * the result is an empty list.
     */
    static inline void INIT_LIST_HEAD(struct list_head *list)
    {
    	WRITE_ONCE(list->next, list);
    	WRITE_ONCE(list->prev, list);
    }
    
  • 添加节点到链表

    • list_add
      把新节点添加到链表头。
    /**
     * 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);
    }
    
    • list_add_tail
      把新节点添加到表尾。
    /**
     * 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);
    }
    
  • 从链表中删除某个节点

    • list_del
    /**
     * 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.
     */
    static inline void list_del(struct list_head *entry)
    {
    	__list_del_entry(entry);
    	entry->next = LIST_POISON1;
    	entry->prev = LIST_POISON2;
    }
    
  • 替换链表中的某个节点

    • list_replace
    	/**
     * list_replace - replace old entry by new one
     * @old : the element to be replaced
     * @new : the new element to insert
     *
     * If @old was empty, it will be overwritten.
     */
    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;
    }
    
  • 合并链表

    • list_splice(struct list_head *list,struct list_head *head)
      合并两个链表,它将 list 指向的链表的每个节点插入到指定链表的 head 元素后面(不会修改 list 链表本身)。
    • list_splice_init(struct list_head *list,struct list_head *head)
      与list_splice函数一致,但是会重新init list,相当与删除原链表。
  • 链表遍历

    • list_entry
    /**
     * list_entry - get the struct for this entry
     * @ptr:	the &struct list_head pointer.
     * @type:	the type of the struct this is embedded in.
     * @member:	the name of the list_head within the struct.
     */
    #define list_entry(ptr, type, member) \
    	container_of(ptr, type, member)
    
    • list_for_each_safe
      获取结构体指针需要使用list_entry(),可以参考示例。
    /**
     * list_for_each_safe - iterate over a list safe against removal of list entry
     * @pos:	the &struct list_head to use as a loop cursor.
     * @n:		another &struct list_head to use as temporary storage
     * @head:	the head for your list.
     */
    #define list_for_each_safe(pos, n, head) \
    	for (pos = (head)->next, n = pos->next; \
    	     !list_is_head(pos, (head)); \
    	     pos = n, n = pos->next)
    
    // 参考使用示例
    getframe_deferred(struct aoedev *d, u32 tag)
    {
          struct list_head *head, *pos, *nx;
          struct frame *f;
    
          head = &d->rexmitq;
          list_for_each_safe(pos, nx, head) {
                  f = list_entry(pos, struct frame, head);
                  if (f->tag == tag) {
                          list_del(pos);
                          return f;
                  }
          }
          return NULL;
    }
    
    • list_for_each_entry
    /**
     * 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_head within the struct.
     */
    #define list_for_each_entry(pos, head, member)				\
    	for (pos = list_first_entry(head, typeof(*pos), member);	\
    	     !list_entry_is_head(pos, head, member);			\
    	     pos = list_next_entry(pos, member))
    

0x03:双向链表使用示例

  • 示例代码
//list.c
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>

/* header of list */
#include <linux/list.h>

/* private structure */
struct node {
    const char *name;
    struct list_head list;
};

/* Initialize a group node structure */
static struct node node0 = { .name = "BiscuitOS_node0", };
static struct node node1 = { .name = "BiscuitOS_node1", };
static struct node node2 = { .name = "BiscuitOS_node2", };
static struct node node3 = { .name = "BiscuitOS_node3", };
static struct node node4 = { .name = "BiscuitOS_node4", };
static struct node node5 = { .name = "BiscuitOS_node5", };
static struct node node6 = { .name = "BiscuitOS_node6", };

/* Declaration and implement a bindirect-list */
LIST_HEAD(BiscuitOS_list);

static __init int list_entry_init(void)
{
  struct node *np;

  /* add a new entry on special entry */
  list_add_tail(&node0.list, &BiscuitOS_list);
  list_add_tail(&node1.list, &BiscuitOS_list);
  list_add_tail(&node2.list, &BiscuitOS_list);

  printk("BiscuitOS_list:\n");
  /* Traverser all node on bindirect-list */
  list_for_each_entry(np, &BiscuitOS_list, list)
    printk("%s\n", np->name);

  /* get the struct for this entry */
  np = list_entry(BiscuitOS_list.next, struct node, list);
  printk("The entry struct node: %s\n", np->name);

  /* get the first element from a list */
  np = list_first_entry(&BiscuitOS_list, struct node, list);
  printk("The first entry struct node: %s\n", np->name);

  return -1;
}


static void __exit list_entry_exit(void)
{
  printk("Bye bye list_entry\n");
}

module_init(list_entry_init);
module_exit(list_entry_exit); 
MODULE_LICENSE("GPL");

Makefile:

obj-m += list.o

KBUILD_CFLAGS+= -g

all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
  • 测试效果
[ 2113.170373] BiscuitOS_list:
[ 2113.170374] BiscuitOS_node0
[ 2113.170374] BiscuitOS_node1
[ 2113.170375] BiscuitOS_node2
[ 2113.170376] The entry struct node: BiscuitOS_node0
[ 2113.170376] The first entry struct node: BiscuitOS_node0
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值