linux通用链表
引言
链表的实现是基于结构体与指针两者实现的,常用的链表数据结构如下:
//将int起别名ELEMTYPE,是为了方便修改链表中的数据域类型。
typedef int ELEMTYPE;
typedef struct list
{
ELEMTYPE data;
struct list *prev, *next;
struct list;
}NODE, *pLIST;
如上链表设计与本身的数据域相关性太大,很难适应不同类型数据域代码的通用。
在Linux中设计了一种适合于各种类型数据域都可以使用的通用型链表:
struct list_head {
struct list_head *prev, *next;
};
摒弃掉数据域,只保留头尾指针。
内核链表
链表的主要意义就是将分散地址的数据域通过指针排列成有序的队列。因此数据域是链表不可或缺的一部分,但是在实际使用中需要不同类型的数据域,因此也就限制了链表的通用。Linux中在声明中抛弃了数据域,也就解决掉了这一问题。
原理
Linux使用链表的方法:使用时,自定义结构体包含数据域+链表结构体。即让内部链表成员与其他链表成员构建成双链表,实现遍历寻址,然后通过链表成员找到包含该成员的结构体首地址。
链表.png
如上图所示,将结构体A、B、C中的内核结构体指针构建成双链表,这样结构体A、B、C中的链表成员就可以互相索引了。
「数据结构实现:」
/* Declare a user structure containing a general double-linked list */
typedef struct circle_queue {
int id;
struct list_head my_list;
}NODE;
「链表初始化:」 链表的通用操作,先初始化链表,然后逐个增加节点。这里具体操作不过多描述,尾附代码。
/*
* Initialize a doubly linked list
*/
static inline void init_list_head(struct list_head *list)
{
list->next = list;
list->prev = list;
}
NODE* queue_init()
{
NODE *head = (NODE *)malloc(sizeof(NODE));
init_list_head(&head->my_list);
return head;
}
「内部数据域访问:」 在实现以上操作,就可以实现结构体A、B、C中链表成员的索引遍历了。既然能访问到结构体A、B、C内部成员,自然也可以通过地址换算得到结构体A、B、C的首地址;进而得到A、B、C的数据域成员。
「linux实现获取结构体首地址:」
#define list_entry(ptr, type, member)
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
结构体地址.png
如图所示,(unsigned long)(&((type *)0)->member))),将0地址强制转化为struct circle_queue类型,则此时0虚拟出了本结构体首地址。(unsigned long)(&((type *)0)->member)))就为member首地址,同样也是A的大小。
因为A与B类型相同,所以A=B。pos指向的首地址减去B的大小就为结构体x的首地址。即((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))返回的是链表所在结构体的首地址。结构体首地址拿到后,其他成员的访问不在话下。
「通过上述方法, 可以通过任一结构体成员获取到该结构体的首地址」
其余操作
剩下的就是链表的通用操作:增、删、改、查。「增:」
/*
* 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)
{
new->next = next;
new->prev = prev;
prev->next = new;
next->prev = new;
}
static inline void list_add(struct list_head *head, struct list_head *new)
{
__list_add(new, head, head->next);
}
-------------------------------------------------------------------------------
void queue_add(NODE *p, NODE *new)
{
list_add(&p->my_list, &new->my_list);
}
「删:」
/*
* 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)
{
prev->next = next;
next->prev = prev;
}
static inline void list_del(struct list_head *del)
{
__list_del(del->prev, del->next);
}
-------------------------------------------------------------------------------
/* Delete node at specified location */
void queue_del(NODE *p, int num)
{
int i = 0;
NODE *del;
struct list_head *plist;
plist = &p->my_list;
for (i = 0; i < num; i++) {
plist = plist->next;
}
del = list_entry(plist, typeof(*del), my_list);
list_del(plist);
free(del);
}
「改:」
/*
* Get the first address from the structure member
*/
#define list_entry(ptr, type, member)
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
-------------------------------------------------------------------------------
void queue_modify(NODE *p, int num, int data)
{
int i = 0;
NODE *mdfy;
struct list_head *plist;
plist = &p->my_list;
for (i = 0; i < num; i++) {
plist = plist->next;
}
mdfy = list_entry(plist, typeof(*mdfy), my_list);
mdfy->id = data;
}
「查:」
/*
* Get the first address from the structure member
*/
#define list_entry(ptr, type, member)
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
#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))
-------------------------------------------------------------------------------
/* Show all of node */
void queue_show(NODE *head)
{
NODE *pos;
printf ("id: n");
list_for_each_entry(pos, &head->my_list, my_list) {
printf("%d ", pos->id);
}
}
「测试效果:」
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Linux 系统中查找正在运行的进程的完整命令、当前工作目录等信息的方法
- Go by Example 中文:通道方向
- mycat数据库集群系列之mysql主从同步设置
- Tun/Tap接口使用指导
- Swift中? 、! 和 ??
- 故障分析 | 记一次 MySQL 主从双写导致的数据丢失问题
- 集成 SpringBoot 2.3.2 + Shiro 1.5.3 + jwt (无状态)
- 技术译文 | MySQL 8.x DDL 和查询重写插件
- iOS webp图片展示处理
- Android内存优化 | LeakCanary/Profiler & 非静态内部类耗时操作 实战分析
- 使用keycloak实现k8s用户权限的统一管理
- python魔法方法是什么
- 如何同步上游分支代码?
- 在 Pycharm 中安装及使用 Jupyter (图文详解)
- 【经验分享】如何使用keras进行多主机分布式训练