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) + ;
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

linux C Obstack的更多相关文章

  1. Linux 驱动开发

    linux驱动开发总结(一) 基础性总结 1, linux驱动一般分为3大类: * 字符设备 * 块设备 * 网络设备 2, 开发环境构建: * 交叉工具链构建 * NFS和tftp服务器安装 3, ...

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

    Obstack是C标准库里面对内存管理的GNU扩展 Obstack介绍 Obstack初始化 在Obstack中申请对象 释放对象 申请growing object 获取Obstack状态 数据对齐 ...

  3. Linux ARM交叉编译工具链制作过程【转】

    本文转载自:http://www.cnblogs.com/Charles-Zhang-Blog/archive/2013/02/21/2920999.html 一.下载源文件 源代码文件及其版本与下载 ...

  4. Linux调试工具

    1. 使用printf调试 #ifdef DEBUG Printf(“valriable x has value = %d\n”, x) #endif 然后在编译选项中加入-DDEBUG 更复杂的调试 ...

  5. 【Linux开发】OpenCV在ARM-linux上的移植过程遇到的问题4---共享库中嵌套库带路径【已解决】

    [Linux开发]OpenCV在ARM-linux上的移植过程遇到的问题4-共享库中嵌套库带路径[已解决] 标签:[Linux开发] 紧接着上一篇,我居然又尝试了一下编译opencv,主要是因为由于交 ...

  6. Linux mlocate源码分析:updatedb

    在Linux的文件查找命令中,mlocate提供的locate命令在单纯进行路径名名查找时有着显著的效率优势,因为mlocate预先对磁盘文件进行扫描并存储到一个数据库文件中,查找时只需要检索数据库而 ...

  7. Linux 内核概述 - Linux Kernel

    Linux 内核学习笔记整理. Unix unix 已有40历史,但计算机科学家仍认为其是现存操作系统中最大和最优秀的系统,它已成为一种传奇的存在,历经时间的考验却依然声名不坠. 1973 年,在用 ...

  8. 死磕内存篇 --- JAVA进程和linux内存间的大小关系

    运行个JAVA 用sleep去hold住 package org.hjb.test; public class TestOnly { public static void main(String[] ...

  9. NodeJs在Linux下使用的各种问题

    环境:ubuntu16.04 ubuntu中安装NodeJs 通过apt-get命令安装后发现只能使用nodejs,而没有node命令 如果想避免这种情况请看下面连接的这种安装方式: 拓展见:Linu ...

随机推荐

  1. 【Bash百宝箱】Linux shell学习

    shell特点-- Linux有多种shell能够使用,默认的为bash,bash有以下几个主要特点. 1.命令记忆能力 在命令行中按上下键能够找到一个前/后输入的命令.这些命令记录在-/.bash_ ...

  2. hdu 1558 Segment set (并查集)

    Segment set Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Tota ...

  3. [Postgres] Filter Data in a Postgres Table with Query Statements

    We have all this data, but how do we answer questions about it? In this lesson we’ll learn how to fi ...

  4. 【codeforces 546A】Soldier and Bananas

    time limit per test1 second memory limit per test256 megabytes inputstandard input outputstandard ou ...

  5. c#代码01--控制台的简单输入与输出及日期的格式输出

    /* 使用ReadLine()完成控制台的输入输出内容 */ using System; namespace Test { class Test1 { static void Main(string[ ...

  6. ZOJ Monthly, June 2014 解题报告

    A.Another Recurrence Sequence problemId=5287">B.Gears 题目大意:有n个齿轮,一開始各自为一组.之后进行m次操作,包含下面4种类型: ...

  7. NOIP模拟 拆网线 - 贪心策略+dp

    题目大意: 给一颗n个节点的树,保留最少的边,使得每个连通块的大小都大于等于2,并且连通块的点数和等于k. 题目分析: 要想留下的边数最少,就要尽量多的选择单独的边,这里就要贪心:尽可能多的选择单独的 ...

  8. centos安装启动ssh服务

    centos安装启动ssh服务 #rpm -qa |grep ssh 检查是否装了SSH包 没有的话yum install openssh-server #chkconfig --list sshd ...

  9. SharePoint 2010/2013 隐藏的速度下拉菜单列表项

    SharePoint 2010/2013 隐藏的速度下拉菜单列表项         有时为了防止一些用户编辑列表项.需要隐藏下拉菜单列表项.,仅仅须要添加一个内容编辑器控件,将css代码写入其HTML ...

  10. WPF 插拔触摸设备触摸失效

    原文:WPF 插拔触摸设备触摸失效 最近使用 WPF 程序,在不停插拔触摸设备会让 WPF 程序触摸失效.通过分析 WPF 源代码可以找到 WPF 触摸失效的原因. 在 Windows 会将所有的 H ...