`
hulianwang2014
  • 浏览: 691523 次
文章分类
社区版块
存档分类
最新评论
  • bcworld: 排版成这样,一点看的欲望都没有了
    jfinal

适用于连续资源块的数组空闲链表的算法

 
阅读更多
如何来管理空闲资源,显而易见的是组织成一个双向链表,称作freelist,然后每次从该链表上取出一个,释放的时候再放回去。为了减少碎片,最好的策略就是优先分配最近释放掉的那个,如果能考虑合并的话,类似伙伴系统那样,就再好不过了,本文给出的是一个通用的可以将资源映射到一个整型ID的资源分配算法,完全基于一个数组,不需要内存管理,也不需要分配结构体。
组织链表的时候,内存管理要耗去大量的工作,前向指针和后向指针的修改前提是必须有这些指针。典型的数据结构就是Linux内核的list_head结构体。但是对于静态的类似位图的资源并不适合用list_head来组织,因为这类资源本身可以映射到一块连续的以自然数计数的ID,比较典型的就是磁盘的空闲块,连续内存块的分配。
既然资源位置是连续的,它就一定能用连续的自然数来表示,那么所有的资源就可以表示成一个数组了-其映射成自然数的ID的数组,记为ArrayA。
接下来我们需要另外一个数组来表示空闲链表,记为ArrayB。
接下来的然后,就是构造ArrayB了...ArrayB的大小等于ArrayA大小加上1,多出来的这个元素可以作为不动点存在,它是不会被分配出去的。ArrayB的元素的大小是ArrayA数组大小占据字节数的两倍,是为了在一个元素中存储两个INDEX,比如数组大小可以用8位数据表示,即最多256个元素,那么ArrayB的元素就应该是2*8这么大的,举例说明:
数组大小:short-最多65536个元素
ArrayA的数组定义:int arrayA[MAX];MAX最大65536
ArrayB的数组定义:int arrayB[];int型为两个short型

结构体形象化表示ArrayB:
#define NUM    8
struct freeHL {
        short high_preindex; //表示前一个ArrayA数组中索引
        short low_nextindex;  //表示后一个ArrayA数组中索引
};
struct freeHL  freelist[NUM+1];

相当于将ArrayB劈开成了两半。
然后就可以在连续的数组空间进行链接操作了。实际上这个数组表示的freelist和指针表示的prev,next的freelist是一致的,数组下标也是一个指针,只是在数组表示的freelist中,使用的是相对指针偏移而已,表示为下标!
下面就是一个算法实现问题了,很简单。在freelist中分配了一个index后,需要修改其前向index的后向index以及后向index的前向index,释放过程和分配过程相反。代码如下:
short data[NUM]; //是为ArrayA
struct freeHL  freelist[NUM+1]; /是为ArrayB
//表示一个下一个分配的index;
unsigned int position = 0;

//全局的一次性初始化,注意,如果是序列化到了文件,
//则不能再次初始化了,应该从文件反序列化来初始化。
void list_init()
{
        int i = 0, j = -1;
        for (; i < NUM+1; i++) {
                freelist[i].high_preindex = (i + NUM)%(NUM+1);
                freelist[i].low_nextindex = (i + 1)%(NUM+1);
        }
        position = 0;
}
//分配接口
int nalloc()
{
        int ret_index = -1, next_index = -1, pre_index = -1;
        ret_index = position;
    //保存当前要分配index的前向index
        next_index = freelist[position].low_nextindex;
    //保存当前要分配index的后向index
        pre_index = freelist[position].high_preindex;
    //分配当前index
        position = next_index;
        if (ret_index == next_index) {
                return -1;
        }
    //更新当前index前向index的后向index
        freelist[freelist[ret_index].high_preindex].low_nextindex = next_index;
    //更新当前index后向index的前向index
        freelist[freelist[ret_index].low_nextindex].high_preindex = pre_index;
        return ret_index;
}
//释放接口
int nfree(unsigned int id)
{
        int pos_pre = -1, pos_next = -1;
    //保存下一个要分配的index的前向index,减少碎片以及更加容易命中cache
        pos_pre = freelist[position].high_preindex;
    //释放index为id的元素
        freelist[pos_pre].low_nextindex = id;
        freelist[id].high_preindex = pos_pre;
        freelist[id].low_nextindex = position;
    //下一个要分配的index为刚释放的index
        position = id;
}

下面是一个测试:
int main(int argc, char **argv)
{
        list_init();
        int i = 0;
        printf("begin\n");
        for (; i < NUM+1; i++) {
                printf("\n%d \n", nalloc());
        }
        nfree(5);
        nfree(0);
        nfree(7);
        for (i = 0; i < NUM+1; i++) {
                printf("\n%d \n", nalloc());
        }
        printf("\nend\n");
}

这个代码用在何处呢?前面说过,用在资源可以表示为连续ID的场合,这种场合何在?在《编写一个UNIX文件系统》中,我说那个空闲i节点以及空闲块的分配算法不好,而上述的方法就可以用,效果比较好,也就是说,将一点点的工作施放于每次分配/释放的时候,就可以避免在某个时间点做大量的积攒下来的繁重的工作。这个算法省去了遍历操作,代价就是占用了一点连续的地址空间。
分享到:
评论

相关推荐

    动态分区管理----用C语言(也可以用Java)实现采用首次适应算法的内存分配和回收过程。

    定义管理空闲分区的相关数据结构:采用空闲分区链表来管理系统中所有的空闲分区,链表中的每个节点表示一个空闲分区,登记有空闲分区的起始地址和长度。 定义一个简单的进程控制块,其中有对应进程分配到的内存的...

    c语言数据结构算法演示(Windows版)

    右侧显示存储空间,空白表示空闲块,其他颜色覆盖表示占用块,在存储空间不足分配时将进行空闲块压缩。左侧显示存储映像。弹出窗口为输入窗口,由用户输入请求分配的空间大小和分配或释放块的块名。 32. 静态查找 ...

    一种Linux多线程应用下内存池的设计与实现

    对内存池中内存块获取、分配机制、内存块大小、内存释放,以及在多线程环境下的安全处理等细节进行了研究,保证了在多线程环境下能够快速同时采用一种基于数组的链表机制,改进内存池中内存块的查找算法,将其时间...

    C语言模拟实现操作系统内存的分配与回收

    本次实验采用C编写,将内存空间定义为结构体链表,成员有作业名name[20]、作业首址s_add、作业长度length及下一节点的指针...空闲分区表定义为结构体数组,成员有空闲区首址s_add、空闲区长度length、表项状态state。

    algorithms:只是个菜鸟。这个项目是在空闲时间编写的。我会不定期更新。希望与您分享一些东西〜

    与大家分享我学习算法的...数组/链表:树相关:AVLTree 平衡二叉搜索树BinaryHeap 二叉堆(优先队列)Num2TreeSum 数组树的和MaxDepth4Tree (leetcode 104)ValidBinarySearchTree (leetcode 98)SameTree (leetcode 100)...

    用c描述的数据结构演示软件

    本系统对屏幕设计的基本原则是集数据结构、算法和其他重要信息(如栈等)于同一屏幕。一般情况下演示屏由图示、算法和变量三个窗口组成,特殊情况下将根据演示内容适当增加。一般情况下, 左侧图示窗口显示演示数据...

    数据结构演示软件

    本系统对屏幕设计的基本原则是集数据结构、算法和其他重要信息(如栈等)于同一屏幕。一般情况下演示屏由图示、算法和变量三个窗口组成,特殊情况下将根据演示内容适当增加。一般情况下, 左侧图示窗口显示演示数据...

    数据结构课程设计

    但如果5、6号窗口全忙,而2、3、4号窗口有空闲时,理财卡业务也可以在空闲的2、3、4号窗口之一办理。 客户领号、业务完成可以作为输入信息,要求可以随时显示6个营业窗口的状态。 5、4阶斐波那契序列如下:f0=f1=f2=...

    计算机二级公共基础知识

    在线性链表中,各数据元素结点的存储空间可以是不连续的,且各数据元素的存储顺序与逻辑顺序可以不一致。在线性链表中进行插入与删除,不需要移动链表中的元素。 线性单链表中,HEAD称为头指针,HEAD=NULL(或0)...

    P2P视频技术源码(VC)

    SP和CP的处理有所不同, 对于CP, 块是以hash的方式来存放的, 例如, 块的ID为1000, 而 max_queue为100, 则存储位置为1000%100=0. 对于SP, 如果资源是一个CS发来的频道, 则是一个循环队列, 每一块按照次序分别存放在...

    P2P视频播放器 详细制作实例

    SP和CP的处理有所不同, 对于CP, 块是以hash的方式来存放的, 例如, 块的ID为1000, 而 max_queue为100, 则存储位置为1000%100=0. 对于SP, 如果资源是一个CS发来的频道, 则是一个循环队列, 每一块按照次序分别存放在...

    华为编程开发规范与案例

    随机值的背后往往隐藏着指针问题,两块内存缓冲区的交界处比较容易出现问题,在编程时是应该注意的地方。 【案例1.2.3】 【正 文】 在接入网产品A测试中,在内存数据库正常的情况下的各种数据库方面的操作都是...

    超级有影响力霸气的Java面试题大全文档

    静态INCLUDE用include伪码实现,定不会检查所含文件的变化,适用于包含静态页面&lt;%@ include file="included.htm" %&gt; 26、什么时候用assert。 assertion(断言)在软件开发中是一种常用的调试方式,很多开发语言中都...

    java 面试题 总结

    静态INCLUDE用include伪码实现,定不会检查所含文件的变化,适用于包含静态页面&lt;%@ include file="included.htm" %&gt; 23、什么时候用assert。 assertion(断言)在软件开发中是一种常用的调试方式,很多开发语言中都...

    oracle学习文档 笔记 全面 深刻 详细 通俗易懂 doc word格式 清晰 连接字符串

    数据库(Database)是按照数据结构来组织、存储和管理数据的仓库,它产生于距今五十年前。简单来说是本身可视为电子化的文件柜——存储电子文件的处所,用户可以对文件中的数据运行新增、截取、更新、删除等操作。 ...

Global site tag (gtag.js) - Google Analytics