Linux 内核“共享”双向链表
在 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; //星星的x坐标 int y; //星星的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;
typedef struct { int x; //星星的x坐标 int y; //星星的y坐标 enum STATUS stat; //状态 unsigned radius; //星星的半径 int step; //每次跳跃的间隔 int color; //星星的颜色 DbLinkNode node; // 双向链表节点“挂件” }STAR;
实现要点:
使用 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 <Windows.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; int y; enum STATUS stat; unsigned radius; int step; int color; DbLinkNode node; }STAR; bool DbList_Init(DbLinkList &L) { L.next = NULL; L.prev = NULL; 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;//因为这已经生成了(DbLinkNode node不是生成的指针),所以init不用初始化 cl->fd = -1; sl = new STAR; sl->x = -1; DbList_Init(cl->node); DbList_Init(sl->node); //2.尾插法 cout << "尾插法创建双向链表" << endl; cout << "请输入元素个数n:"; cin >> n; cout << "\n请依次输入n个元素的文件句柄:" << endl; while (n > 0) { s = new ConnTimeout; //生成新节点s cin >> s->fd; printf("s 的地址:%p node:%p\n0", 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); } //4.销毁 DbLinkNode* tmp = NULL; 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; } //通过节点访问到节点访问到节点承载的数据 //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); system("pause"); return 0; }