码迷,mamicode.com
首页 > 其他好文 > 详细

Obstack是C标准库里面对内存管理的GNU扩展

时间:2016-08-07 23:24:46      阅读:402      评论:0      收藏:0      [点我收藏+]

标签:

Obstack是C标准库里面对内存管理的GNU扩展

Obstack介绍

Obstack初始化

在Obstack中申请对象

释放对象

申请growing object

获取Obstack状态

数据对齐

以下是来自wiki对obstack的介绍:

  Obstack是C标准库里面对内存管理的GNU扩展(实际上就是GNU C library了)。Obstack===Object stack。没错,Obstack就是一个栈,栈里面的元素是对象object(不是面向对象的对象哦,这里的对象单指数据元素)。这些数据是动态的,也就是使用的是动态内存。这种内存管理技术属于Region-based memory management。

  那什么叫基于区域的内存管理呢?区域指的是我们申请的数据对象存放的位置。在这种内存管理模式下,我们将所有申请的数据对象集中放在一起(当然咯,地址不一定连续。集中在一起是一种逻辑结构,比如Obstack的栈)。好处就是,可以一次性的释放内存,集中管理。这下你对Obstack有了大体上的了解了吧。

以下为GNU C的Obstack描述:

  Obstack是一种内存池,内存池里面包含了数据对象栈。

  啥叫内存池呢?内存池也叫做固定大小的块内存申请(fixed-size blocks allocation),同样也是一种内存管理技术。这种技术允许动态申请内存。(是不是迷糊了?前面还说固定,现在又说动态了。实际上固定的是总大小,动态是指内存实际申请使用。如果你用过virtual box,没错就是那个百分之60刚玩linux的人都会安装的东西。里面创建虚拟硬盘时对动态分配有这样的描述:虚拟磁盘只是逐渐占用物理硬盘空间[直至达到分配的大小],不过当其内部空间不用时不会自动缩减暂用的物理硬盘空间])这种方式有点类似malloc或者C++的new操作。但是malloc和new操作都会造成内存碎片化问题(别打我,这是wiki讲的,不关我的事)。更有效地方式就是使用相同大小内存池了。预先申请,集中管理。应用程序在内存池里面自由的玩耍,从来不上岸-_-|||。

  回过头继续讲Obstack。你可以创建任意数量的独立的Obstack,然后在这些Obstack里面申请对象。这些对象遵循栈的逻辑结构:最后申请的对象必须第一个释放。不同的Obstack里面的数据是相互独立的,没有任何关系。除了对内存释放顺序的要求之外,obstack是非常通用的:一个Obstack可以包含任意数量任意尺寸的对象。

  Obstack里面内存申请的实现用的一般都是宏,很有效率吧。如果你不想使用宏(理由是:方便调试),可以看一看我的另一篇文章。这篇文章的最后介绍了如何避免使用预定义的宏。唯一的内存空间损耗就是不同对象之间可能需要内存填充,让每一个对象都起始于合适的边界线,这一点实际上是用空间换取时间。

  Obstack的表现形式是一个结构体:struct obstack。这个结构有一个非常小的固定大小,记录了obstack的状态,以及如何找到该Obstack里的对象。但是呢,obstack结构体自身不包含任何对象,别妄想直接探寻struct obstack的内容了。必须使用规定的函数才行,这点没得商量。

  你可以通过声明一个struct obstack类型的变量来创建obstack。或者动态申请struct obstack *obstack = (struct obstack *)malloc(sizeof(struct obstack)); 你还可以obstack里面使用obstack(然并卵,GNU C library文档自己说的)

  Obstack使用的函数都需要指定使用的是那个obstack。Obstack里面的对象会被打包成一个大的内存块(叫做chunks)。struct obstack结构指针指向当前使用的chunks。

  每当你申请的对象无法塞进之前的chunk时,都会创建一个新的chunk。这些chunk以何种形式连接在一起(链表?树?),不是我们关心的啦。这些交由obstack自动管理。chunk由obstack自动创建,但创建的方式得由你来决定,一般会直接或间接的用到malloc函数。

  联想我们之前讲到的内存池:chunk是固定大小的池。Obstack只是一种池的管理员:他告诉来洗澡的人必须遵守规定,最后来的必须先走。

 

Obstack初始化:

#include<obstack.h>
int obstack_init(struct obstack *obstack-ptr)

 

 

  obstack_init实际上是一个宏实现。obstack_init会自动调用obstack_chunk_alloc函数来申请chunk,注意这还是个宏,需要你指定这个宏指向的函数。如果内存申请失败了会调用obstack_alloc_failed_handler指向的函数,没错依旧是宏。如果chunk里面的对象都被释放了,obstack_chunk_free指向的函数被用来返回chunk占用的空间。目前版本的obstack_init永远只返回1(之前的版本会在失败的时候返回0)

实例:

技术分享
#include<obstack.h>
#include<stdlib.h>
#define obstack_chunk_alloc malloc
#define obstack_chunk_free free

//静态申请obstack
static struct obstack myobstack;
obstack_init(&myobstack);

//动态申请obstack
struct obstack *myobstack_ptr = (struct obstack*) malloc (sizeof (struct obstack))
obstack_init(myobstack_ptr)
技术分享

    obstack chunk的大小默认情况下为4096。如果你想自定义chunk的大小,需要使用宏obstack_chunk_size

int obstack_chunk_size (struct obstack* obstack-ptr)

   注意这实际上是个左值(C++里面有麻烦的左值引用,右值引用 囧rz)。obstack-ptr如之前的myobstack所示,指明了是哪个obstack。如果你适当的提高chunk的大小,有利于提高效率。减小则没啥好处。。。

if (obstack_chunk_size (myobstack_ptr) < new-chunk-size)
    obstack_chunk_size (myobstack_ptr) = new-chunk-size

 

在Obstack里面申请对象:

   最直接的方法使用aobstack_alloc函数

void * obstack_alloc (struct obstack *obstack-ptr, int size)

 

  调用方式和malloc差不多, 对新申请的空间不初始化。如果chunk被object填满了,obstack_alloc会自动调用obstack_chunk_alloc。

技术分享
struct obstack string_obstack;
/*
....
初始化内容
....
*/
char * copystring (char *string)
{
    size_t len = strlen (string) + 1;
    char *s = (char*) obstack_alloc (&string_obstack, len);
    memcpy (s, string , len);
    return s;
}
技术分享

 

  如果想在申请时初始化,需要用到obstack_copy或者obstack_copy0

void * obstack_copy (struct obstack *obstack-ptr, void *address, int size)
//使用从地址address开始复制size大小的数据初始化,新申请的object内存大小也为size

void *obstack_copy0 (struct obstack *obstack-ptr, void *address, int size)
//同obstack_copy但结尾处额外添加一个空字符,在复制以NULL结尾的字符串时很方便。

 

 

  释放Obstack里面的对象:

  同样很简单,我们使用函数obstack_free

void obstack_free  (struct obstack *obstack-ptr, void *object)

 

  如果object是空指针的话,所有的对象都被释放,留下一个未初始化的obstack,然后obstack库会自动释放chunk。如果你指定了object地址,那自这个object之后的所有对象都被释放。值得注意的是:如果你想释放所有object但同时保持obstack有效,可以指定第一个object的地址

obstack_free (obstack_ptr, first_object_allocated_ptr);

 

  

可增长内存空间的对象:

  由于obstack chunk的内存空间是连续的,在其中的对象空间可以一步步搭建一直增加到结尾。这种技术称之为growing object。尽管obstack可以使用growing object,但是你无法为已申请好的对象使用这项技术(理由是如果这对象之后还有对象,会造成冲突)

技术分享
void obstack_blank (struct obstack *obstack-ptr, int size)
//添加一个为初始化的growing object

void obstack_grow (struct obstack *obstack-ptr, void *data, int size)
//添加一个初始化的空间,类似与obstack_copy

void obstack_grow0 (struct obstack *obstack-ptr, void *data, int size)
//obstack_grow类似与obstack_copy, 那obstack_grow0你说类似与谁?

void  obstack_lgrow (struct obstack *obstack-ptr, charc)
//一次添加一个字符(字节)

void    obstack_ptr_grow (struct obstack *obstack-ptr, void *data)
//一次添加一个指针。。。大小为(sizeof(void *)

void obstack_int_grow (struct obstack *obstack-ptr, int data)
//一次添加一个int型数据,大小为sizeof(int)
技术分享

 

  在growing object中最重要的函数是obstack_finish, 因为只有调用了本函数之后才会返回growing object 的最终地址。

void *obstack_finish (struct obstrack *obstrack-ptr)

 

  如果你想获取growing object当前大小,可以使用obstack_object_size

int obstack_object_size (struct obstack *obstrack-ptr)
//只能用在obstack_finish之前,否则只会返回0

 

  如果你想取消一个正在增长的对象,必须先结束他,然后再释放。示例如下:

obstack_free (obstack_ptr, obstack_finish (obstack_ptr))

 

  上面的函数在添加数据时会检查是否由足够的空间。如果你只增加一丁点的空间,检查这一步骤显然是多余的。我们可以省略这一步骤,更快的growing。这里关于growing object额外在介绍一个函数:

int obstack_room (struct obstack *obstack-ptr)
//返回可以安全添加的字节数

 

  其余的快速添加函数只需要在之前的growing object函数添加后缀_fast即可(如果不清楚可以查看GNU C 关于这部分的介绍)

 

Obstack 的状态:

以下函数用于获取obstack的状态:

技术分享
void * obstack_base (struct obstack *obstack-ptr)
//返回正在增长的对象的假定起始地址。为啥是假定呢,因为如果你增长的过大,当前chunk的空间不够,obstack就会新创建一个chunk,这样地址就变了。


void *obstack_next_free (struct obstack *obstack-ptr)
//返回当前chunk未被占用的第一个字节的地址

int obstack_object_size (struct obstack *obstack-ptr)
//返回当前growing object的大小。等同与:
//obstack_next_free (obstack-ptr) -obstack_base (obstack-ptr)
技术分享

 

 

Obstack 里的数据对齐问题

int obstack_alignment_mask (struct obstack *obstack-ptr)

 

  宏展开后是一个左值。如果是函数实现,返回的是掩码。你给他复制也应该是掩码。掩码的值应该是2的n次方减一,换算后也就是说地址必须是2的n次方的倍数。如果你改变了掩码,只有下次申请object的时候才会生效(特例是growing object:立即生效,调用obstack_finish后就能看到效果了)

 本文地址http://www.cnblogs.com/san-fu-su/p/5739780.html

Obstack是C标准库里面对内存管理的GNU扩展

标签:

原文地址:http://www.cnblogs.com/Leo_wl/p/5747560.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!