Linux 内核“共享”双向链表(高逼格代码)

27 篇文章 1 订阅

在 linux 内核中,有大量的数据结构需要用到双向链表,例如进程、文件、模块、页面等。 若采用双向链表的传统实现方式,需要为这些数据结构维护各自的链表,并且为每个链表都 要设计插入、删除等操作函数。因为用来维持链表的 next 和 prev 指针指向对应类型的对 象,因此一种数据结构的链表操作函数不能用于操作其它数据结构的链表。 比如,我们需要分别定义星星和 web 服务器超时的链表结构 :

//一. web 服务器超时的链表结构
	typedef struct {
		int fd;
		time_t timeout; // 使用超时时刻的时间戳表示
	}ConnTimeout;

	struct Link_Node
	{
		ConnTimeout conn;
		struct Link_Node* next;
	}


	typedef struct {
		int x;
		int y; 
		enum STATUS stat;
		unsigned radius; 
		int step; 
		int color; 
	}STAR;
	struct Link_Node {
		STAR star;
		struct Link_Node* next;
	}

有没有一种方式,可以让多个链表共享同一套链表的操作?请看下图:
在这里插入图片描述
在这里插入图片描述

typedef struct _DoubleLinkNode {
		struct _DoubleLinkNode* next; //下一个节点的指针域 
		struct _DoubleLinkNode *prev; //上一个结点的指针域 
	}DbLinkNode;

	typedef struct {
		int fd; 
		time_t timeout; // 使用超时时刻的时间戳表示 
		DbLinkNode node; // 双向链表节点“挂件” 
	}ConnTimeout;

如何访问挂载的数据,原理如下

在这里插入图片描述

实现要点

使用 offsetof 可以根据链表节点在结构体中的地址逆推出结构体变量的位置.

typedef struct { 
int fd ; 
time_t timeout; // 使用超时时刻的时间戳表示 
DbLinkNode node; // 双向链表节点“挂件” 
}ConnTimeout;

通过节点访问承载数据

//通过节点访问到节点承载的数据 
	ConnTimeout *ct = new ConnTimeout; 
	DbLinkNode *p = &(ct->node);
	
	cout<<"请输入超时节点对应的 fd: ";
	cin>>ct->fd;
	
	cout<<"\n 通过链表中的节点访问节点上承载的数据:"<<endl;
	int offset = offsetof(ConnTimeout, node);
	
	ConnTimeout *tmp = (ConnTimeout *)((size_t)p-offset);
	printf("offset: %d\n", offset);
	printf("通过链表节点 node 访问到的数据:%d\n", tmp->fd);

源码实现

#include<iostream> 
#include<string> 
#include<stdlib.h> 

using namespace std;

typedef struct _DoubleLinkNode {
	//int data; //结点的数据域 
	struct _DoubleLinkNode* next; //下一个节点的指针域 
	struct _DoubleLinkNode* prev; //上一个结点的指针域 
}DbLinkNode, DbLinkList; //LinkList 为指向结构体 LNode 的指针类型 

typedef struct {
	int fd;
	time_t timeout; // 使用超时时刻的时间戳表示
	DbLinkNode node; // 双向链表节点“挂件” 
}ConnTimeout;

typedef struct {
	int x; //星星的 x 坐标 
	int y; //星星的 y 坐标 
	enum STATUS stat; //状态 
	unsigned radius; //星星的半径 
	int step; //每次跳跃的间隔 
	int color; //星星的颜色 
	DbLinkNode node; // 双向链表节点“挂件” 
}STAR;

bool DbList_Init(DbLinkList& L)//构造一个空的双向链表 L 
{
	L.next = NULL; //头结点的 next 指针域置空 
	L.prev = NULL; //头结点的 prev 指针域置空 
	return true;
}

//尾插法 
bool DbListInsert_back(DbLinkList& L, DbLinkNode& node) {
	DbLinkNode* last = NULL;
	last = &L;

	while (last->next) last = last->next;

	node.next = NULL;
	last->next = &node;
	node.prev = last;

	return true;
}
int main(void) {
	ConnTimeout* cl = NULL, * s = NULL;
	STAR* sl = NULL;
	int n = 0;

	//1.初始化一个空的双向链表 
	cl = new ConnTimeout;
	cl->fd = -1;

	sl = new STAR;
	sl->x = -1;

	DbList_Init(cl->node);
	DbList_Init(sl->node);

	//2.使用尾插法插入数据 
	cout << "尾插法创建双向链表" << endl;
	std::cout << "请输入元素个数 n:";
	cin >> n;
	cout << "\n 请依次输入 n 个元素的文件句柄:" << endl;

	while (n > 0) {
		s = new ConnTimeout; //生成新节点 s 

		cin >> s->fd;
		printf("s 的地址:%p node: %p\n", s, &(s->node));
		DbListInsert_back(cl->node, s->node);
		n--;
	}

	//3.根据链表节点访问数据 
	DbLinkNode* p = NULL;

	p = &(cl->node);
	cout << "遍历连接超时链表中的节点:" << endl;

	while (p) {
		int offset = offsetof(ConnTimeout, node);
		ConnTimeout* ct = (ConnTimeout*)((size_t)p - offset);
		cout << ct->fd << endl;
		p = p->next;
	}

	//4. 销毁双向链表 
	p = &(cl->node);
	cout << "销毁连接超时链表中的节点:" << endl;
	while (p) {
		int offset = offsetof(ConnTimeout, node);

		ConnTimeout* ct = (ConnTimeout*)((size_t)p - offset);
		printf("offset: %u ct: %p p:%p\n", offset, ct, p);
		cout << ct->fd << endl;
		p = p->next; delete ct;
	}

	system("pause");
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值