linux内核双向链表学习
来源:互联网 发布:伍聚网络股票 编辑:程序博客网 时间:2024/05/24 08:34
在linux内核中双向链表普遍存在,因此,理解链表的添加,删除,替换,遍历等操作很重要。
1、概述
这里主要讲述对链表里宏定义说明、基本操作,下面是链表的基本框图(摘自百度图片)。
2、宏定义说明
(1)offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 计算结构体成员MEMBER在结构体TYPE中的偏移量
(TYPE *)0将0地址强制转化为TYPE类型结构体
(TYPE *)0)->MEMBER指向结构体成员
&((TYPE *)0)->MEMBER获取成员地址,由于结构体TYPE的地址为0,所以成员地址就是该成员在TYPE中的偏移量。
((size_t) &((TYPE *)0)->MEMBER)将地址转化为size_t数据类型
(2)container_of
具体定义如下(src/include/linux/kernel.h):
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
根据结构体type的成员变量member指针ptr地址推导出结构体type变量的起始地址指针。typeof:类似sizeof获取参数的类型,有两种形式:表达式和类型。参数为表达式,则表达式不执行返回该表达式的类型,extern int foo();typeof(foo()) var;等价于int var。参数为数据类型,typeof(int *)p1, p2;等价于int *p1, *p2。
typeof( ((type *)0)->member )由于(type *)0)->member为表达式,因此typeof返回该member的数据类型
const typeof( ((type *)0)->member ) *__mptr = (ptr); 定义一个数据类型同member的常量指针,并指向ptr指向的地址。
(char *)__mptr 将指针转化为字符型指针,地址可转化到1字节
offsetof(type,member)成员member在结构体type中的偏移量
(char *)__mptr - offsetof(type,member) mptr(即结构体type中member真实地址)-member在结构体中的偏移量,得到该结构体type的起始地址。
(type *)( (char *)__mptr - offsetof(type,member) )将该地址转化为结构体type指针
3、linux内核链表操作函数
(1)涉及文件
src/include/linux/list.h
src/include/linux/types.h
基本思路:它是将双向链表节点嵌套在其它的结构体中;在遍历链表的时候,根据双链表节点的指针获取"它所在结构体的指针",从而再获取数据
(2)节点定义
struct list_head {
struct list_head *next, *prev;
};
内核使用:一般会使用该结构体定义一个双向链表头,同时在其他结构体中 嵌套该结构体,使其他结构体成为双向链表的一个节点。
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
LIST_HEAD新建一个双向链表表头都指向自己,LIST_HEAD_INIT和INIT_LIST_HEAD意义一样。
(3)添加节点
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);
}
一个是向链表头添加,一个向链表末尾添加。
(4)删除节点
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
static inline void __list_del_entry(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
(5)获取节点
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
根据结构体type的成员变量member指针ptr地址推导出结构体type变量的起始地址指针,详细见上一节说明。
(6)循环获取节点
#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))
pos:结构体指针变量,head:双向链表头变量,member:结构体成员变量类型为struct list_head
typeof(*pos):根绝结构体指针pos获取结构体类型
pos = list_entry((head)->next, typeof(*pos), member):根据链表头获取链表第一个元素
&pos->member != (head):判断双向链表是否循环结束
pos = list_entry(pos->member.next, typeof(*pos), member):获取下一个链表元素
4、简单example
定义实际结构体:
struct family
{
int num;
int sex;
int age;
char* name;
struct list_head list;
};
定义双向链表头:LIST_HEAD(family_head)
定义结构体实体:struct family father, mother, son;
添加到链表:list_add(&father.list, &family_head), list_add_tail(&mother.list, &family_head), list_add_tail(&son_list, &family_head),整个链表就是family_head <=>father<=>mother<=>son<=>family_head。
搜寻整个链表:
定义结构体指针 struct family *pfamily
list_for_each_entry(pfamliy, family_head, list) 每次返回的就是father, mother, son的起始地址
- linux内核双向链表学习
- Linux内核中的双向循环链表学习
- linux内核双向循环链表实例
- Linux内核双向循环链表分析
- linux内核源码“双向链表list_head”
- Linux内核双向链表的实现
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】详解Linux内核之双向循环链表
- Linux内核-双向循环链表代码分析
- 详解Linux内核之双向循环链表(转载)
- 详解Linux内核之双向循环链表
- 详解Linux内核之双向循环链表(转)
- 深入浅出linux内核源代码之双向链表list_head(上)
- 深入浅出linux内核源代码之双向链表list_head(下)
- linux 内核分析之list_head 双向链表结构
- 深入浅出linux内核源代码之双向链表list_head(上)
- 深入浅出linux内核源代码之双向链表list_head(下)
- linux内核的数据结构:1 双向循环链表
- 详解Linux内核之双向循环链表
- 欢迎使用CSDN-markdown编辑器
- 第十周项目3--利用二叉树遍历思想解决问题--判断二叉树相似
- 堆与栈在内存里是怎么分配的?
- 进程进入不了下一function
- UVA 11729-Commando War(排序分任务)
- linux内核双向链表学习
- N-armed bandit problem
- SpringMVC入门案例——注解配置方式
- OpenCV函数estimateRigidTransform 使用心得
- 【UVALive 7505】Hungry Game of Ants(DP)
- 数学之美3
- Oracles数据库 修改表名 列名 字段类型 语句
- Android_listview_video安卓列表视频直接播放
- PendingIntent与Intent的区别