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:
文件Makefile:
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
相关文章推荐
- 解析从源码分析常见的基于Array的数据结构动态扩容机制的详解
- C#数据结构揭秘一
- Java数据结构之简单链表的定义与实现方法示例
- qqwry.dat的数据结构图文解释第1/2页
- Java数据结构之双端链表原理与实现方法
- Huffman 编码压缩算法
- C 的反思穷究链表
- 数据结构&算法学习
- 戴文的Linux内核专题:02 源代码
- 戴文的Linux内核专题:03 驱动程序
- 第二话:数据结构的历史与来由
- 第一话:你的数据结构怎么学的?
- 鸟瞰数据结构的知识点全貌
- Linux内核中用d_path得到绝对路径名
- Android对Linux内核的改动你知道多少
- 数据结构的基本概念和术语
- selinux介绍及关闭方式!
- 开发语言、开发工具、数据结构和算法的关系
- Linux 2.6内核配置选项注解
- 排序算法总结