用一个链表连接不同类型结构体
来源:互联网 发布:怎么应聘淘宝客服 编辑:程序博客网 时间:2024/06/06 02:46
一直以来对操作系统的内核感兴趣,对操作系统中进程、线程、互斥资源、内存等资源的管理方式充满好奇,最近看了一个学习系统的内核代码,惊奇于它管理方式的奇特,更佩服系统开发者的思维之奇特。现在把它对各种对象的管理方式记录下来,供以后查询源代码时做参考。
在操作系统中,每种资源(如进程、互斥资源等)都对应有一个结构体也就是一种对象的类型,而每种资源都对应有N多对象。如何管理好操作系统中各种不同的类型和各种类型所对应的对象就成为一个难题了,但是在该操作系统中这个问题解决的很好。我也是第一次见到这种奇特的管理方式。
在介绍操作系统如何管理好操作系统中形形色色的对象类型和对象之前,先要复习一下数据结构中的内容——双向循环链表。如下图所示:
为了避免忘记,将部分源代码记录下来:(这只不过是一个双向循环链表的基本操作)
#include <iostream> using namespace std; //该结构体没有数据域,只有两个指针域。 typedef struct M_LIST_ENTRY { M_LIST_ENTRY *pre; M_LIST_ENTRY *next; }M_LIST_HEAD; //初始化双向循环链表 void Init(M_LIST_HEAD* pListHead) { pListHead->pre = pListHead->next = pListHead; } //将某个结构体插入至对应链表头 void InsertIntoHead(M_LIST_HEAD* pListHead, M_LIST_ENTRY* pListEntry) { pListHead->next->pre = pListEntry; pListEntry->next = pListHead->next; pListHead->next = pListEntry; pListEntry->pre = pListHead; pListHead = pListEntry; }
下图描述了当该操作系统中存在三种对象类型和若干个对象时,由对象类型和对象组成的链表。这里只列出三种链表,分别是对象类型链表、对象链表以及属于某种对象类型的对象链表。对象类型链表的链表头是 ObpTypeListHead,管理了所有对象类型,该链表在全局是唯一的,并且是一个单向链表。对象类型链表中链接了三种对象类型,分别是 Process 对象类型、Thread 对象类型和 Mutex 对象类型。对象链表的链表头是 ObpObjectListHead,它管理了系统中所有的对象,该链表在全局是唯一的并且是一个双向循环链表。为什么要用双向循环链表呢?因为双向循环链表的优势就在于,当需要从链表中移除一个链表项时,只需要有这个链表项的指针即可完成操作。
总结:系统中每创建一个对象类型,都会链接在ObpTypeListHead中;每个对象都链接在ObpObjectListHead中。直接上图:
当然这并不是重点,重点是上图如何用代码的形式表示出来,即如何将不同类型的对象连接起来,下面就详细的介绍一下原理,并用代码的形式表示出来。
还记得上面的双向循环链表的结构体不?
typedef struct M_LIST_ENTRY { M_LIST_ENTRY *pre; M_LIST_ENTRY *next; }M_LIST_HEAD;
这个结构体只有指针域,并没有实际的数据域。当然我们要的就是这个东西啦。下面看看如何利用它来串联不同结构体对象的。
首先我们要将上面的结构体嵌入其他结构体中。看下面的定义:
typedef struct Mem1 { size_t size; M_LIST_ENTRY mHead1; }*pMem1; //////////////////////////////////////////////////////////////////////////////////// typedef struct Mem2 { char ch; M_LIST_ENTRY mHead2; }*pMem2; ////////////////////////////////////////////////////////////////////////////////////
这两个不同的结构体中都有一个Head节点,所有这两种类型所创建的对象都插入到对应的链表中。分别由Mem1、Mem2创建一个对象m1、m2,将这两种对象连接起来就用如下的方法:
M_LIST_HEAD Mhead;//创建一个M_LIST_HEAD类型的头结点用于连接其他对象Init(&Mhead);Mem1 m1;Mem2 m2;m1.size = 10;m2.ch = 'a'; Init(&m1.mHead1);Init(&m2.mHead2);//将这两种对象都插入到以Mhead为头结点的链表中InsertIntoHead(&Mhead, &m1.mHead1);InsertIntoHead(&Mhead, &m2.mHead2);
这样就将这两种不同的对象用M_LIST_ENTRY域连接起来了,这样是爽了,但是我要怎么访问这个对象中所有域呢?我现在只知道M_LIST_ENTRY域啊!接下来要解决的就是这个问题了。在C语言中变量内存布局一文中已经有解决方案了,现在就用一个简单的例子来验证一下吧!
typedef unsigned long UNLONG_PTR; struct Node { size_t size; size_t data; size_t account;}*M,node;
假设我们现在只知道account域的地址。&M->account,首先来看看->是如何获取数据的。
“->”操作符首先计算出域 account在M中的偏移值,与 该结构体对象M指向的地址(基址)相加得到地址,再访问内存中的数据。下面就是利用这个原理来获取基址的。
(Node*)((UNLONG_PTR)&node.account - (UNLONG_PTR)&((Node*)0)->account)
其中&((Node*)0)->account得到的是当基址为0时,account域的偏移值。和&node.account相减得到的当然就是M的基址了。因此就可以访问所有的数据成员了。
值得一提的是一定要将地址转化成10进制数据然后进行运算,因为两个地址相减得到的只是两个地址间的距离(4字节)。和16进制相减是不同的!切记切记!!
地址:0xF004 - 地址:0xF000 = 1 16进制 = 0xF004
具体代码如下:
#include <iostream>using namespace std;struct Node{size_t size;size_t data;size_t account;};typedef unsigned long UNLONG_PTR;void main(){Node node;node.size = 1;node.data = 2;node.account = 3;cout<<&node<<endl<<endl;cout<<&node.size<<endl;cout<<&node.data<<endl;cout<<&node.account<<endl;cout<<"---------------------------"<<endl;cout<<"Address = "<<((&node.account - &((Node*)0)->account))<<endl;cout<<"Address * 4 = "<<((&node.account - &((Node*)0)->account))*4<<endl;cout<<(UNLONG_PTR)&node.account - (UNLONG_PTR)&((Node*)0)->account<<endl;cout<<"---------------------------"<<endl;Node* p =(Node*) ((UNLONG_PTR)&node.account - (UNLONG_PTR)&((Node*)0)->account);cout<<p->account<<" "<<p->data<<" "<<p->size<<endl;system("pause");}
- 用一个链表连接不同类型结构体
- SQL 中不同类型的表连接
- oracle sql 中不同类型的表连接
- 不同类型的线性链表
- 在socket通信中当用recv接收不同类型的结构体后,区分是哪种结构体。
- 设计模式-连接不同类型
- 用一个数组存放不同类型指针的想法
- 关于不同类型的结构体的数组的读取和保存的测试程序
- C语言中不同类型的结构体的指针间可以强制转换
- 面对不同类型的数值,我们如何使用结构体进行赋值
- 不同类型对象的内存结构比较
- 【读书笔记】【收获,不止Oracle】不同类型下表连接限制的对比
- LINQ不同类型数据源连接的问题
- 结构体指针连接
- sql语句 一个表 用户产生两条不同类型的数据 拼接成一条数据
- 用一个方法返回两个及两个以上不同类型的结果
- 如何在一个链表中链入不同类型的对象
- 关于树形结构的表与另外一个表的连接查询
- linux产看端口占用情况
- HelloWindows
- SQL语句之左联接例子
- 如何引导编译器动态链接第三方软件
- Visual C++ 生成的各种文件
- 用一个链表连接不同类型结构体
- visual assistx 破解
- 项目经验分享——Java常用工具类集合
- RedHat安装LibreOffice
- 特性和类别
- 编程分别输入两个按从小到大排序的数组a和b,将这两个有序数组合并,使合并后的数组仍有序 (从小到大)
- android中给TextView或者Button的文字添加阴影效果
- 20大让人啼笑皆非的歧义域名
- 20 年来科技领域十大遮丑门:欲盖弥彰