您的位置:首页 > 运维架构 > Linux

linux内核入门之list介绍(1)

2013-11-01 11:57 423 查看
1.介绍

linux内核中双向链表的核心数据结构struct list_head如下:

<linux/list.h>

struct list_head {

        struct list_head *next, *prev;

};

从上面定义可以看出,linux将双向链表关系关系抽离成单独的数据结构,只要包含了list_head类型成员的对象都能成为链表节点,这样就可以将具体的数据类型与数据之间的关系解耦合。

如果把包含list_head的对象称为节点,那么:

next指向下个节点的list_head;

prev指向上个节点的list_head。

使用list_head时,一般将list_head嵌套到自己的结构体中。如下:

struct my_struct {

        struct list_head list;

        void *data

};

链表使用前需要初始化,有两种初始化方式:

a.动态初始化,则运行时初始化

struct my_struct *p;

p = (struct my_struct *)kmalloc(sizeof(struct my_struct), GFP_KERNEL);

INIT_LIST_HEAD(&p->list);

p->data = NULL;

INIT_LIST_HEAD是一个inline函数。

b.静态初始化,即编译时初始化

struct my_struct mine = {

        .list = LIST_HEAD_INIT(mine.list),

        .data = NULL

};

LIST_HEAD_INIT是一个宏。

使用LIST_HEAD(list)直接创建一个list节点.

2.链表操作

如果把head当作头节点,则head->next为first节点,head->prev为last节点。

     +---------------------------------------------------------------------+

     |     +-----------+       +-----------+              +----------+     |

     +-----|   prev    | <-----|   prev    |<---      <---|   prev   | <---+

           +-----------+       +-----------+     ----     +----------+

     +---> |   next    | ----->|   next    |--->      --->|   next   | ----+

     |     +-----------+       +-----------+              +----------+     |

     +---------------------------------------------------------------------+

              head                  first                      last

a.添加新节点

list_add(struct list_head *new, struct list_head *head);

list_add_tail(struct list_head *new, struct list_head *head);

list_add将new节点添加在head与head->next之间;而list_add_tail则添加在head->prev与head之间。

b.删除节点

list_del(struct list_head *entry);

list_del_init(struct list_head *entry);

删除entry节点后,list_del把entry->next赋值为LIST_POISON1,entry->prev赋值为LIST_POISON2;

而list_del_init对entry执行了INIT_LIST_HEAD(entry)操作,即将entry的prev和next指针指向自身.

c.移动节点

list_move(struct list_head *list, struct list_head *head);

list_move_tail(struct list_head *list, struct list_head *head);

list_move节点list移到head之后,成为first节点;而list_move_tail则移动了head之前,即成为last节点.

d.判断链表是否为空

list_empty(struct list_head *head);

e.拼接两个链接

list_splice(struct list_head *list, struct list_head *head);

list_splice_init(struct list_head *list, struct list_head *head);

list_splice将list的first到last链表拼接到head与head->next之间;list_splice_init同时把list执行了INIT_LIST_HEAD操作.

f.获取包含list_head的结构体

list_entry(ptr, type, member)

prt : 为结构体中list_head指针;

type : 为结构体类型,如struct my_struct;

member : 为结构体内的list_head成员名称.

struct my_struct *my;

my = list_entry(p, struct my_struct, list);

g.遍历链表

list_for_each(pos, head)

list_for_each_safe(pos, n, head)

pos : 用于存放每次遍历时的元素指针

head : 遍历的链表头节点

list_for_each_safe用n指针临时存放pos的下个节点,避免被删除操作破坏.

struct list_head *p;

struct my_struct *my;

list_for_each(p, &mine->list) {

        my = list_entry(p, struct my_struct, list);

}

3.示例

a.目录结构

|--list_test.c

|--Makefile

b.源代码

文件list_test.c:

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

struct my_struct {
struct list_head list;
char ch;
};

static void prt_list(const char *msg, struct list_head *head)
{
struct list_head *p;
struct my_struct *my;

printk(KERN_ALERT "%s", msg);
list_for_each(p, head) {
my = list_entry(p, struct my_struct, list);
printk(KERN_ALERT "%c\n", my->ch);
}
}

static int test_init(void)
{
struct my_struct a, b, c, d;
struct my_struct oa, ob, oc;

/*define head node*/
LIST_HEAD(head);
LIST_HEAD(ohead);

a.ch = 'a';
b.ch = 'b';
c.ch = 'c';

list_add(&a.list, &head);
list_add(&b.list, &head);
list_add(&c.list, &head);

prt_list("print list:\n", &head);

list_del(&b.list);
prt_list("after delete 'b':\n", &head);

d.ch = 'd';
list_add_tail(&d.list, &head);/*add 'd' to tail */
prt_list("after add 'd' to tail:\n", &head);

list_move(&d.list, &head);/*move 'd' to head*/
prt_list("after move 'd' to head:\n", &head);

oa.ch = 'A';
ob.ch = 'B';
oc.ch = 'C';
list_add_tail(&oa.list, &ohead);
list_add_tail(&ob.list, &ohead);
list_add_tail(&oc.list, &ohead);
prt_list("print other list:\n", &ohead);

list_splice_init(&ohead, &head);
prt_list("after splice other list to first list:\n", &head);
if (list_empty(&ohead))
printk(KERN_ALERT "other list is empty\n");

return 0;
}

static void test_exit(void)
{
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ice");

/*
print list:
c
b
a
after delete 'b':
c
a
after add 'd' to tail:
c
a
d
after move 'd' to head:
d
c
a
print other list:
A
B
C
after splice other list to first list:
A
B
C
d
c
a
other list is empty
*/


文件Makefile:

obj-m := list_test.o

KERNELDIR ?= /lib/modules/`uname -r`/build
PWD := `pwd`

default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息