linux内核编程之list_head

【版权声明:转载请保留出处:blog.csdn.net/gentleliu。邮箱:shallnew*163.com】

linux内核头文件 #include 中定义了一个list_head类型的结构体:

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

一般我们的链表节点都包含许多成员,要使用list_head结构体来构造链表的话需要将该结构体嵌入节点结构,可以声明节点结构体如下:
struct todo_struct {
    int                 count;
    struct list_head    list;
};

节点之间时通过list成员来进行连接。

使用前需要首先声明一个链表头,并初始化。

struct list_head    todo_list;
INIT_LIST_HEAD(&todo_list);

初始化的实现如下:
static inline void INIT_LIST_HEAD(struct list_head *list)
{
	list->next = list;
	list->prev = list;
}

向该链表 添加节点使用list_add和list_add_tail函数

list_add(&pnode->list, &todo_list);//在头部添加
list_add_tail(&pnode->list, &todo_list);//在尾部添加

其中第一个参数是将要加入到链表的节点结构体中的list_head成员,第二个参数为该链表的头结点或链表中某一个节点中list_head成员,新的节点将在第二个参数的前面或后面加入。

函数实现如下:
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;
}

static inline void list_add(struct list_head *new, struct list_head *head)
{
	__list_add(new, head, head->next);
}

static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
	__list_add(new, head->prev, head);
}


删除链表节点使用list_del函数

list_del(struct list_head *entry);

该函数删除拥有参数list_head结构的链表节点。
该函数实现如下:
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
	next->prev = prev;
	prev->next = next;
}

static inline void list_del(struct list_head *entry)
{
	__list_del(entry->prev, entry->next);
	entry->next = NULL;
	entry->prev = NULL;
}

判断链表是否为空使用函数list_empty,实现如下:
static inline int list_empty(const struct list_head *head)
{
	return head->next == head;
}
当我们遍历该链表时是通过链表list_head中两个的成员来寻找的,但是我们得到的也仅仅是节点结构体中list_head结构体的地址,我们需要得到的是整个结构体的地址,并通过该地址得到该结构体里面的其他成员的值。怎么办呢?我们可以使用宏list_entry(struct list_head *ptr, type_of_struct, field_name)来达到这个目的,该宏可以将list_head指针映射回包含它的大结构指针。
ptr指向正在被使用的list_head的指针,type_of_struct是包含list_head的大结构体类型,field_name是大结构体中拥有list_head类型的变量名字,在本例中为list。
struct todo_struct      *pnode = NULL;
struct list_head        *ptr = NULL;
pnode = list_entry(ptr, struct todo_struct, list);
此处可以通过传入的list_head指针p得到的拥有该指针的大结构体pnode地址,此时就可以取得结构体中任意变量的值了。
一般我们遍厉一个链表都使用一个循环来实现,如下:
for (ptr = todo_list.next; ptr != &todo_list; ptr = ptr->next) {
	pnode = list_entry(ptr, struct todo_struct, list);
	// ......
}

事实上内核实现了遍厉链表的宏:
#define list_for_each(pos, head) \
	for (pos = (head)->next; prefetch(pos->next), pos != (head); \
		pos = pos->next)
所以上面代码可以修改为:
list_for_each(pos, head) {
	pnode = list_entry(p, struct todo_struct, list);
	// ......
}

当我们循环遍厉时可能会删除节点,此时可以使用另外一个宏:
list_for_each_safe(struct list_head *curser, struct list_head *next, struct list_head *list);

他只是简单的在循环开始处把链表下一项存储在next中。
其实现如下:
#define list_for_each_safe(pos, n, head) \
	for (pos = (head)->next, n = pos->next; pos != (head); \
		pos = n, n = pos->next)

事实上list_head还实现了将list_for_each_safe)和list_entry合起来使用的宏:
list_for_each_entry(type cursor, struct list_head *list, member);
list_for_each_entry_safe(type cursor, type next, struct list_head *list, member);

使用该宏就不需要在循环内调用lit_entry了。
其实现如下:
#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))

#define list_for_each_entry_safe(pos, n, head, member)			\
	for (pos = list_entry((head)->next, typeof(*pos), member),	\
		n = list_entry(pos->member.next, typeof(*pos), member);	\
	     &pos->member != (head);					\
	     pos = n, n = list_entry(n->member.next, typeof(*n), member))


下面给一个我的一个简单使用示例:

#include 
#include 
#include 

#include 

#include 
// #include 
#include 

struct todo_struct {
    int                 count;
    struct list_head    list;
};

struct list_head    todo_list;
//rwlock_t list_lock = RW_LOCK_UNLOCKED;

void sln_list_create(void)
{
    int                 i = 0;
    struct todo_struct  *pnode = NULL;

    for (i = 0; i < 10; i++) {
        pnode = (struct todo_struct *)kmalloc(sizeof(struct todo_struct), GFP_ATOMIC);
        if (NULL == pnode) {
            return;
        }

        pnode->count = i;

//        write_lock(&list_lock);
        list_add_tail(&pnode->list, &todo_list);
//        write_unlock(&list_lock);
    }
}

void sln_list_del(int val)
{
    struct todo_struct      *pnode = NULL;
    struct list_head        *p = NULL;

//    write_lock(&list_lock);
    list_for_each(p, &todo_list) {
        pnode = list_entry(p, struct todo_struct, list);
        if (pnode->count == val) {
            list_del(p);
//            write_unlock(&list_lock);
            return ;
        }
    }
//    write_unlock(&list_lock);
}

void sln_list_del2(int val)
{

    struct todo_struct      *pnode = NULL;
    struct list_head        *p = NULL, *next = NULL;

//    write_lock(&list_lock);
    list_for_each_safe(p, next, &todo_list) {
        pnode = list_entry(p, struct todo_struct, list);
        if (pnode->count == val) {
            list_del(p);
//            write_unlock(&list_lock);
            return ;
        }
    }
//    write_unlock(&list_lock);
}

void sln_list_traverse(void)
{
    struct todo_struct      *pnode = NULL;
    struct list_head        *p = NULL;

//    read_lock(&list_lock);
    list_for_each(p, &todo_list) {
        pnode = list_entry(p, struct todo_struct, list);
        printk(KERN_ALERT"%d\n", pnode->count);
    }
    printk(KERN_ALERT"\n");
//    write_lock(&list_lock);
}

void sln_list_traverse2(void)
{
    struct todo_struct      *pnode = NULL;

    list_for_each_entry(pnode, &todo_list, list) {
        printk(KERN_ALERT"%d\n", pnode->count);
    }
}

static __init int sln_list_init(void)
{
    printk(KERN_ALERT"=====%s=====\n", __func__);

    INIT_LIST_HEAD(&todo_list);

    sln_list_create();
    sln_list_traverse();

    sln_list_del(6);
    sln_list_traverse();

    sln_list_del2(4);
    sln_list_traverse2();

    return 0;
}

static __exit void sln_list_exit(void)
{
    printk(KERN_ALERT"=====%s=====\n", __func__);
}

module_init(sln_list_init);
module_exit(sln_list_exit);

MODULE_LICENSE("GPL");





你可能感兴趣的:(linux,内核编程系列)