Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1838350
  • 博文数量: 184
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 2388
  • 用 户 组: 普通用户
  • 注册时间: 2016-12-21 22:26
个人简介

90后空巢老码农

文章分类

全部博文(184)

文章存档

2021年(26)

2020年(56)

2019年(54)

2018年(47)

2017年(1)

我的朋友

分类: LINUX

2020-01-12 22:57:41

在堆上分配内存

进程可以通过增加堆的大小来分配内存,所谓堆,就是一段长度可以变的连续虚拟内存,始于进程的未初始化数据末尾。通常将堆的当前内存边界称为program break

改变堆的大小,其实就像命令内核改变进程的program break位置一样,program break位置抬升后,程序可以访问新分配区域的任何内存地址,而此时物理内存页尚未分配。内核会在进程首次试图访问这些虚拟内存地址的时候,自动分配新的物理页内存

点击(此处)折叠或打开

  1. #include <unistd.h>
  2. /*将program break设置为参数end_data_segment所指定的位置,四舍五入到下一个内存页的边界处*/
  3. int brk(void *end_data_segment);
  4. /*returns 0 on success, or -1 on error*/
  5. /*将program break 在原有地址上增加从参数increment传入的大小*/
  6. void *sbrk(intptr_t increment);//sbrk(0)返回当前program break的当前位置
  7. /*returns previous program break on success, or (void *)-1 on error*/

点击(此处)折叠或打开

  1. #include <stdlib.h>
  2. void *malloc(size_t size);// memory aligned
  3. /*returns pointer to allocated memory on success, or NULL on error*/
  4. void free(void *ptr);// ptr can be NULL
一般情况下,free()并不降低program break的位置,而是将这块内存添加到空闲内存链表中,供后续的malloc()函数循环使用,除非释放的部分位于program break当前的位置且足够大时,才会降低program break
  • malloc()实现
首先,malloc()在分配内存块的时候,会额外分配几个自己来存放记录这块内存大小的整数值。该整数位于内存块的起始处,而实际返回给调用者的内存地址恰好位于这一长度记录字节字后

先臊面之前有free()所释放的空闲内存块列表,以求能够找到尺寸大于或等于要求的一块空闲内存。若大,进行分割,返回合适大小,将剩余加入空闲列表
如果空闲列表中没有合适大小的空闲块,调用sbrk()以分配更多的内存,为了减少对sbrk()的调用次数,malloc()并没有严格按照所需字节来分配内存,而是以更大幅度(虚拟页大小的倍数)来增加program break,并将超出的部分置于空闲内存列表
  • free()实现
当free()将内存块置于空闲链表之上时,会使用内存块本身的空间来存放两个(prev和next)链表指针,将自身添加到列表中
glibc提供的malloc()调试工具

  • mtrace(), muntrace()
分别在程序中打开和关闭对内存分配调用进行跟踪的功能。这些函数要与环境变量MALLOC_TRACE搭配使用,该变量定义了写入跟踪信息的文件名。还提供一个脚本(mtrace)用于分析生成文件,并生成易于理解的汇总报告。出于安全原因,设置用户id和组id的程序会忽略对于mtrace()的调用
  • mcheck(), mprobe()
允许程序对已分配内存进行一致性检查。例如,当程序视图在已经分配的内存之外进行写操作时,它会捕获这个错误。使用这些函数的程序,必须使用cc -lmcheck 选项与mcheck库链接
  • MALLOC_CHECK_环境变量
提供了类似于mcheck(), mprobe()函数的功能。通过对其设置不同的值,可以控制程序对内存分配错误的相应方式:0->忽略;1->标准错误打印;2->调用abort()异常终止

valgrind和insure++ 能够发现许多堆内存分配之外的其他类型错误


点击(此处)折叠或打开

  1. #include <stdlib.h>
  2. //参数分别表示分配对象的数量,和每个对象的大小, 会将内存初始化为0
  3. void *calloc(size_t numitems, size_t size);
  4. /*returns pointer to allocated memory on success, or NULL on error*/


点击(此处)折叠或打开

  1. #include <stdlib.h>
  2. void *realloc(void *ptr, size_t size);
  3. /*returns pointer to allocated memory on */

参数ptr指向需要调整大小的内存块的指针,参数size指定所需要调整大小的期望值

点击(此处)折叠或打开

  1. nptr = realloc(ptr, newsize);
  2. if(nptr == NULL){
  3.     /*handle error*/
  4. }else{
  5.     ptr = nptr;//这里不用ptr直接接受返回值就是为了防止出错的时候,仍旧可以对原内存块进行操作
  6. }
分配堆砌的没存:memalign()和posix_memalign()

点击(此处)折叠或打开

  1. #include <malloc.h>
  2. void *memalign(size_t boundary, size_t size);
  3. /*returns pointer to allocated memory on success, or NULL on error*/

memalign()分配size个字节的内存,起始地址是参数boundary的整数倍,而boundary必须是2的整数次幂。

点击(此处)折叠或打开

  1. #include <stdlib.h>
  2. int posix_memalign(void **memptr, size_t alignment, size_t size);
  3. /*returns 0 on success, or a positive error number on error*/
其分配内存地址通过参数memptr返回,内存块的起始地址是alignment参数的整数倍,alignment必须是sizeof(void *)与2的整数次幂两者之间的乘积

点击(此处)折叠或打开

  1. int s;
  2. void *memptr;
  3. s = posix_memalign(&memptr, 1024 * sizeof(void *), 65536);
  4. if(s!=0)
  5.     /*handle error*/





在堆栈上分配内存


点击(此处)折叠或打开

  1. #include <alloca.h>
  2. void *alloca(size_t size);
  3. /*returns pointer to allocated block of memory*/
alloca()是通过增加栈帧的大小从堆栈上分配内存的,当前调用函数的栈帧位于堆栈的顶部。不需要用free()来释放alloca()分配的内存,也不能调用realloc()调整alloca()分配的内存。不能在一个函数的参数列表中调用alloca(),必须采用如下代码:

点击(此处)折叠或打开

  1. void *y;
  2. y = alloca(size);
  3. func(x, y, z);
alloca()相比于malloc()函数族的优势在于分配快速,编译器会将其当做内联代码处理,并通过调整堆栈指针来实现,不需要维护空闲内存块的链表
阅读(8699) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~