分析linked_lists

分析linked_lists

在内核中使用链表的地方非常多,链表是将所有的节点通过指针串联起来,内核使用双链表。

普通的链表

我们通常认识的链表都这么定义

struct node {
    int data;
    struct node *next;
    struct node *prev;
};

先定义一个数据,再加上一个前向指针,再加上以后后向指针,如果只是使用这个数据结构的话,似乎没有什么问题。

如果许多结构体都要使用链表,这样定义,显得冗余:每个结构体都要加2个指针,还有针对每个结构体写增删查改。很麻烦,既然每个结构体都要使用这2个指针,那我就把这两个指针抽出来,单独处理,这样,数据和操作数据的方法进行了分离,有面向对象编程的思想:

struct node {
    int data;
    struct list list_head;
};

内核链表的组成

内核把数据的链接部分剥离出来,就形成了内核linked_lists API。位于<linux/list.h>

链表API分为初始化,增删查改,遍历。好像对数据结构的操作也就这些,剩下的用到再具体分析。

初始化链表

还是举例说明吧,先定义了结构体,包括int data成员

struct mystruct {
     int data ;
} ;

这个数据结构要使用链表来管理,就要在数据结构中再加入一个链表的变量

struct mystruct {
    int data;
    struct list_head mylist;
};

struct list_head位于linux/type.h,定义如下

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

这样就可以通过mylist.next,mylist.prev来访问相邻的项。

以上相当于双联表的数据结构定义完了,接下来就是多个节点怎么链接起来,只有先链接起来才可以操作他,假设定义了两个结构体,分别初始化他们,内核提供了两种方法

1种方法:
struct mystruct node1 = {
    .data = 99,
    .mylist = LIST_HEAD_INIT(node1.mylist),
};
按照宏展开:
#define LIST_HEAD_INIT(name) { &(name), &(name) }
.mylist = {&node1.mylist, &node1.mylist}

等同于结构体的指针分别指向自己:
.mylist = {
    .*next = &node1.mylist,
    .*prev = &node1.mylist,
},
------------------------------------------------------------------2种方法
struct mystruct node2;
node2.data = 99;
INIT_LIST_HEAD(&node2.mylist);
这是个inline函数:
static inline void INIT_LIST_HEAD(struct list_head *list)
{
        WRITE_ONCE(list->next, list);
        list->prev = list;
}
还是等同于结构体的指针分别指向自己

完成节点的定义,要把节点链接起来,还需要定义一个链表的表头

LIST_HEAD(mylisthead);
展开这个宏:
#define LIST_HEAD(name) \
        struct list_head name = LIST_HEAD_INIT(name)
等于:
struct list_head mylisthead = LIST_HEAD_INIT(mylisthead);
继续展开:
struct list_head mylisthead = {
    .*next = &mylisthead,
    .*prev = &mylisthead,
};
相当于只是定义了头部,这个头部没有数据,只有前向后向指针,分别指向了自己

增加链表节点

接下来把定义的节点链接起来吧,内核提供了list_add

list_add(&node1.mylist, &mylisthead);
lsit_add(&node2.mylist, &mylisthead);

展开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
/*
 * Insert a new entry between two known consecutive entries.
 *
 * This is only for internal list manipulation where we know
 * the prev/next entries already!
 */
static inline void __list_add(struct list_head *new,
                              struct list_head *prev,
                              struct list_head *next)
{
        if (!__list_add_valid(new, prev, next))
                return;

        next->prev = new;
        new->next = next;
        new->prev = prev;
        WRITE_ONCE(prev->next, new);
}
每次双链表增加要移动4次指针

删除链表节点

内核提供了list_del方法,想想如果自己来从双链表删除节点涉及哪些操作,因为是双链表,只需要传一个节点进来就行,自己实现一下

void list_del(struct list_head *node)
{
    /* 定义两个临时变量存储当前节点的prev和next */
    prev = node->prev;
    next = node->next;

    /* 指针变换,相当于删除中间的节点 */
    prev->next = next;
    next->prev = prev;

    /* 将删除的节点指针置为NULL */
    node->prev = NULL;
    node->next = NULL;
}
/**
 * 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;
}

#define LIST_POISON1  ((void *) 0x00100100)
#define LIST_POISON2  ((void *) 0x00200200)

static inline void __list_del_entry(struct list_head *entry)
{
        if (!__list_del_entry_valid(entry))
                return;

        __list_del(entry->prev, entry->next);
}

/*
 * 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;
        WRITE_ONCE(prev->next, next);
}

自己写代码的时候,把链表删除了,就把节点的prev和next置为空,内核却制定到了具体为止难道LIST_POISON,why? 有毒。

遍历链表

如何遍历一个链表:先找到链表头,依次找到next指针,如果这个指针又指回了链表头,说明遍历完了整个链表。

看内核实现:

struct list_head *position = NULL;
struct mystruct *datastructptr = NULL;
LIST_HEAD(mylisthead);1种遍历方法:先遍历链表,然后通过链表找到结构体的头部,再通过头部找到结构体的其他元素。
list_for_each(position, &mylisthead) {
    datastructptr = list_entry(position, struct mystruct, mylist);
    printk(KERN_INFO "data:%d\n", datastructptr->data);
}

遍历链表
list_for_each(position, &mylisthead)

/**
 * list_for_each        -       iterate over a list
 * @pos:        the &struct list_head to use as a loop cursor.
 * @head:       the head for your list.
 */
#define list_for_each(pos, head) \
        for (pos = (head)->next; pos != (head); pos = pos->next)

遍历链表后,找到每个节点的成员,内核用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)2中方法:将上面两步骤合二为一
list_for_each_entry(datastructptr, &mylinkedlist, mylist) {
    printk(KERN_INFO "data:%d\n", datastructptr->data);
}

/**
 * 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);        \
             &pos->member != (head);                                    \
             pos = list_next_entry(pos, member))

先找到第1个成员list_first_entry
/**
 * list_first_entry - get the first element from a list
 * @ptr:        the list head to take the element from.
 * @type:       the type of the struct this is embedded in.
 * @member:     the name of the list_head within the struct.
 *
 * Note, that list is expected to be not empty.
 */
#define list_first_entry(ptr, type, member) \
        list_entry((ptr)->next, type, member)

/**
 * 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_next_entry
/**
 * list_next_entry - get the next element in list
 * @pos:        the type * to cursor
 * @member:     the name of the list_head within the struct.
 */
#define list_next_entry(pos, member) \
        list_entry((pos)->member.next, typeof(*(pos)), member)

最后贴上完整代码

/*
 * kernel linked_list demo
 *
 * (C) 2020.03.26 liweijie<ee.liweijie@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/module.h>
#include <linux/list.h>

static int __init mod_init(void)
{
	struct mystruct {
		int data;
		struct list_head mylist;
		};

	struct mystruct node_1;
	struct mystruct node_2;
	struct mystruct node_3 = {
		.data = 97,
		.mylist = LIST_HEAD_INIT(node_3.mylist),
	};

	struct list_head *position = NULL;
	struct mystruct *datastructptr = NULL;
	LIST_HEAD(mylinkedlist);
	
	node_1.data = 99;
	INIT_LIST_HEAD(&(node_1.mylist));

	node_2.data = 98;
	INIT_LIST_HEAD(&(node_2.mylist));

	list_add(&node_1.mylist, &mylinkedlist);
	list_add(&node_2.mylist, &mylinkedlist);
	list_add(&node_3.mylist, &mylinkedlist);

	list_for_each(position, &mylinkedlist) {
		datastructptr = list_entry(position, struct mystruct, mylist);
		printk(KERN_INFO "data:%d\n", datastructptr->data);
	}

	list_del(&node_1.mylist);
	printk(KERN_INFO "after delete one node\n");
	list_for_each_entry(datastructptr, &mylinkedlist, mylist) {
		printk(KERN_INFO "data:%d\n", datastructptr->data);
	}

	return 0;
}
static void __exit mod_exit(void)
{
	return;
}

module_init(mod_init);
module_exit(mod_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("liweijie<ee.liweijie@gmail.com>");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值