1. brk(); sbrk()

#include <iostream>
#include <cstring>
#include <unistd.h>using namespace std;void brk_test(){printf("堆结束地址:%p\n", sbrk(0));//sbrk(size); 以偏移方式设置堆的结束地址char *p = (char *)sbrk(0);        //偏移值为0,表示当前堆的结束地址brk(p + 4);                     //将堆的结束设置为 p + 4;相当于给p分配了4字节大小的空间printf("堆结束地址:%p\n", sbrk(0));p[0] = 0;p[3] = 3;p[6] = 1;       // 尽管只申请了4个char大小的位置,但是操作p[6]并不会发生错误p[4095] = 1;    // 正常// p[4096] = 1;    // Segmentation faultprintf("%x, %x\n", p[0], p[3]);brk(p + 1);                     //收回3个char大小的空间printf("堆结束地址:%p\n", sbrk(0));p[3] = 1; // 正常brk(p);                         //恢复成最初的结束地址printf("堆结束地址:%p\n", sbrk(0));// p[3] = 1; // Segmentation fault}

输出:

堆结束地址:0x7fffcb9f8000    // 以0x1000(4K, 页)对齐
堆结束地址:0x7fffcb9f8004
0, 3
堆结束地址:0x7fffcb9f8001
堆结束地址:0x7fffcb9f8000

在Linux中,页与页框的大小一般为4KB,页是虚拟内存中若干个大小相等的储存分区,页框则是每页对应的物理存储区;

所以可以看到上面输出的,堆的结束地址应该是4K对齐的(个人理解)。结束地址0x7fffcb9f8000,0x7fffcb9f8000指向的内存并不属于堆。调用 brk(p + 4) 后,相当于在堆上新申请了4字节的空间,由于页对齐的关系,虚拟内存分配到实际的物理内存时只能以4K为最小单位来分配,0x7fffcb9f8000 ~ 0x7fffcb9f8ffff为1页,所以这一页的剩余空余都属于该程序,所以操作p[6]时不会发生段错误,但这还是属于非法操作。而操作p[4096]时,p[4096]的地址是0x7fffcb9f9000,又是新的一页,而这一页并不属于该程序,没有读写权限,所以会报 'Segmentation fault' 错误。最后调用 brk(p) 恢复成初始状态后,系统收回起始地址为0x7fffcb9f8000的页,所以接着操作p[3]报 'Segmentation fault' 错误。

2. malloc(size_t size)

先看一段程序。

int malloc_test(){char *m1 = (char *)malloc(0x10);printf("m1: %p \n", m1);char *m2 = (char *)malloc(0x10);printf("m2: %p \n", m2);char *m3 = (char *)malloc(0x20);printf("m3: %p \n", m3);char *m4 = (char *)malloc(0x30);printf("m4: %p \n", m4);char *m5 = (char *)malloc(0x60);printf("m5: %p \n", m5);printf("m1: %08xh %08xh\n", *((int *)m1 - 1), *((int *)m1 - 2));printf("m2: %08x %08x\n", *((int *)m2 - 1), *((int *)m2 - 2));printf("m3: %08x %08x\n", *((int *)m3 - 1), *((int *)m3 - 2));printf("m4: %08x %08x\n", *((int *)m4 - 1), *((int *)m4 - 2));printf("m5: %08x %08x\n", *((int *)m5 - 1), *((int *)m5 - 2));}

输出:

m1: 0x7fffc71aee70
m2: 0x7fffc71af0a0
m3: 0x7fffc71af0c0
m4: 0x7fffc71af0f0
m5: 0x7fffc71af130
m1: 00000000h 00000021h
m2: 00000000 00000021
m3: 00000000 00000031
m4: 00000000 00000041
m5: 00000000 00000071

使用malloc申请内存时也有地址对齐的情况,0x10对齐。malloc所分配内存的前8字节,其值与申请内存的大小有关,存放的是这一块动态申请的内存的起始地址与下一块动态申请的内存的起始地址的距离(个人理解),再加一。可以约等于系统给你分配的内存大小。(64位测试结果,32位不确定)

3. free(void *ptr)

为什么释放动态内存时只提供首地址就行了,而不用提供具体的大小?

由每块申请的内存的前8个字节可以算出这块内存的大小。

例1.

int malloc_test(){char *m1 = (char *)malloc(0x10);printf("m1: %p \n", m1);char *m2 = (char *)malloc(0x10);printf("m2: %p \n", m2);char *m3 = (char *)malloc(0x20);printf("m3: %p \n", m3);char *m4 = (char *)malloc(0x30);printf("m4: %p \n", m4);char *m5 = (char *)malloc(0x60);printf("m5: %p \n", m5);printf("m1: %08x %08x\n", *((int *)m1 - 1), *((int *)m1 - 2));printf("m2: %08x %08x\n", *((int *)m2 - 1), *((int *)m2 - 2));printf("m3: %08x %08x\n", *((int *)m3 - 1), *((int *)m3 - 2));printf("m4: %08x %08x\n", *((int *)m4 - 1), *((int *)m4 - 2));printf("m5: %08x %08x\n", *((int *)m5 - 1), *((int *)m5 - 2));// *(m5 - 2) = 0x31;free(m3);   // 释放m3printf("\n释放m3后,m3:%08x %08x\n", *((int *)m3 - 1), *((int *)m3 - 2));  // 并没有变化char *m6 = (char *)malloc(0x20);    // 申请和m3相同大小的内存printf("m6: %p \n", m6);printf("m6: %08x %08x\n", *((int *)m6 - 1), *((int *)m6 - 2));}

输出:

m1: 0x7fffbd602e70
m2: 0x7fffbd6030a0
m3: 0x7fffbd6030c0
m4: 0x7fffbd6030f0
m5: 0x7fffbd603130
m1: 00000000 00000021
m2: 00000000 00000021
m3: 00000000 00000031
m4: 00000000 00000041
m5: 00000000 00000071释放m3后,m3:00000000 00000031
m6: 0x7fffbd6030c0
m6: 00000000 00000031

可以看到,free掉m3后,m3前8个字节存的值并没有变化。

再次申请和m3大小相同的m6时,系统把原来m3的内存分配给了m6。

所以malloc并不一定是从堆的尾部开始分配新的内存,优先分配堆内的、大小合适的废弃内存。

例2.

int malloc_test(){char *m1 = (char *)malloc(0x10);printf("m1: %p \n", m1);char *m2 = (char *)malloc(0x10);printf("m2: %p \n", m2);char *m3 = (char *)malloc(0x20);printf("m3: %p \n", m3);char *m4 = (char *)malloc(0x30);printf("m4: %p \n", m4);char *m5 = (char *)malloc(0x60);printf("m5: %p \n", m5);printf("m1: %08x %08x\n", *((int *)m1 - 1), *((int *)m1 - 2));printf("m2: %08x %08x\n", *((int *)m2 - 1), *((int *)m2 - 2));printf("m3: %08x %08x\n", *((int *)m3 - 1), *((int *)m3 - 2));printf("m4: %08x %08x\n", *((int *)m4 - 1), *((int *)m4 - 2));printf("m5: %08x %08x\n", *((int *)m5 - 1), *((int *)m5 - 2));free(m3);   // 释放m3*((int *)m3 - 2) = 0xFF;    // 修改大小标志printf("\n释放m3后,m3:%08x %08x\n", *((int *)m3 - 1), *((int *)m3 - 2));  // 并没有变化char *m6 = (char *)malloc(0x20);    // 申请和m3相同大小的内存printf("m6: %p \n", m6);printf("m6: %08x %08x\n", *((int *)m6 - 1), *((int *)m6 - 2));}

输出:

m1: 0x7fffd5e62e70
m2: 0x7fffd5e630a0
m3: 0x7fffd5e630c0
m4: 0x7fffd5e630f0
m5: 0x7fffd5e63130
m1: 00000000 00000021
m2: 00000000 00000021
m3: 00000000 00000031
m4: 00000000 00000041
m5: 00000000 00000071释放m3后,m3:00000000 000000ff
m6: 0x7fffd5e630c0
m6: 00000000 000000ff

在m3被释放后,修改m3的大小标志,再申请大小相同的m6,系统还是把原来m3的内存分配给了m6。

说明申请内存时,前8个字节并不是用来确定内存块大小的,系统的其他地方还记录着每一块内存的使用情况。

例3.

int malloc_test(){char *m1 = (char *)malloc(0x10);printf("m1: %p \n", m1);char *m2 = (char *)malloc(0x10);printf("m2: %p \n", m2);char *m3 = (char *)malloc(0x20);printf("m3: %p \n", m3);char *m4 = (char *)malloc(0x30);printf("m4: %p \n", m4);char *m5 = (char *)malloc(0x60);printf("m5: %p \n", m5);printf("m1: %08x %08x\n", *((int *)m1 - 1), *((int *)m1 - 2));printf("m2: %08x %08x\n", *((int *)m2 - 1), *((int *)m2 - 2));printf("m3: %08x %08x\n", *((int *)m3 - 1), *((int *)m3 - 2));printf("m4: %08x %08x\n", *((int *)m4 - 1), *((int *)m4 - 2));printf("m5: %08x %08x\n", *((int *)m5 - 1), *((int *)m5 - 2));free(m3);   // 释放m3*((int *)m3 - 2) = 0xff;    // 修改大小标志printf("\n释放m3后,m3:%08x %08x\n", *((int *)m3 - 1), *((int *)m3 - 2));  // 并没有变化char *m6 = (char *)malloc(0x20);    // 申请和m3相同大小的内存printf("m6: %p \n", m6);printf("m6: %08x %08x\n", *((int *)m6 - 1), *((int *)m6 - 2));// *((int *)m3 - 2) = 0x31;free(m6);m6 = (char *)malloc(0x20);    // 申请和m3相同大小的内存printf("m6: %p \n", m6);printf("m6: %08x %08x\n", *((int *)m6 - 1), *((int *)m6 - 2));}

输出:

m1: 0x7ffff5673e70
m2: 0x7ffff56740a0
m3: 0x7ffff56740c0
m4: 0x7ffff56740f0
m5: 0x7ffff5674130
m1: 00000000 00000021
m2: 00000000 00000021
m3: 00000000 00000031
m4: 00000000 00000041
m5: 00000000 00000071释放m3后,m3:00000000 000000ff
m6: 0x7ffff56740c0
m6: 00000000 000000ff
munmap_chunk(): invalid pointer
Aborted (core dumped)

m3的大小标志改成无规律的0xff,先释放m3, 再申请m6后,再释放m6。此时报无效指针的错误‘munmap_chunk(): invalid pointer’。

说明释放内存时,free只根据传入地址的前8个字节来判断内存的块大小,而0xff并不满足内存分配规则,所以报错。

将0xff改成0x21, 0x31...等满足规则的值,程序正常运行。

brk(); sbrk()使用相关推荐

  1. brk(), sbrk() 用法详解【转】

    转自:http://blog.csdn.net/sgbfblog/article/details/7772153 贴上原文地址,好不容易找到了:brk(), sbrk() -- 改变数据段长度 brk ...

  2. linux学习---brk(), sbrk() 用法

    一.brk sbrk介绍 借用linux那个男人,看下 BRK(2)                    Linux Programmer's Manual BRK(2) 此部分补充一个知识点:后面 ...

  3. brk(), sbrk() 用法详解

    贴上原文地址,好不容易找到了:brk(), sbrk() -- 改变数据段长度 brk() , sbrk() 的声明如下: #include <unistd.h> int brk(void ...

  4. C ——进程内存(内存管理、内存分配(brk,sbrk、mmap、munmap)、内存常见错误)

    内存可以说是C++中很重要很重要的一部分了,我相信这也是C++能够排在编程语言前列的一个原因,因为有了内存管理,使得C++在处理一些底层得程序时,能表现得更加得优秀. 1.进程内存应该分为几部分(内存 ...

  5. Linux系统下深究一个malloc/brk/sbrk新内存后的page fault问题

    有耳可听的,就应当听 -<马可福音> 周四的休假团建又没有去,不因别的,只因年前东北行休假太多了,想缓缓-不过真实原因也确实因为假期剩余无几了-思考了一些问题,写下本文.   本文的缘起来 ...

  6. Linux下brk、sbrk实现一个简易版本的malloc

    目录 一.内存四区回顾 二.brk.sbrk 三.使用brk.sbrk模拟实现malloc和free 一.内存四区回顾 程序代码区:该区域在程序运行时存放程序的二进制代码. 全局数据区:该区域主要存放 ...

  7. 操作系统知识整理——Linux下进程的内存布局以及brk()、sbrk()函数探究

    文章目录 前言 一.内存堆栈模型 二.系统栈和用户栈 三.函数调用时的内存栈分配 四.brk(), sbrk() 用法详解 前言 本篇文章是自己在学习xv6操作系统内核时,发现自己对进程在内存中的布局 ...

  8. 【Linux 内核 内存管理】内存管理架构 ④ ( 内存分配系统调用过程 | 用户层 malloc free | 系统调用层 brk mmap | 内核层 kmalloc | 内存管理流程 )

    文章目录 一.内存分配系统调用过程 ( 用户层 | 系统调用 | 内核层 ) 二.内存管理流程 一.内存分配系统调用过程 ( 用户层 | 系统调用 | 内核层 ) " 堆内存 " ...

  9. linux c语言 glibc 错误 munmap,Linux内存分配小结--malloc、brk、mmap

    Linux的虚拟内存管理有几个关键概念: 1.每个进程都有独立的虚拟地址空间,进程访问的虚拟地址并不是真正的物理地址: 2.虚拟地址可通过每个进程上的页表(在每个进程的内核虚拟空间地址)与物理地址进行 ...

最新文章

  1. linux下安装hadoop
  2. Node.js:get/post请求、全局对象、工具模块
  3. Eclipse安装SVN教程
  4. hdu 1516(编辑距离+记录路径)
  5. hibernate 延迟加载的错误 failed to lazily initialize a collection of role
  6. python设计模式之享元模式
  7. HTML+CSS+JS实现 ❤️slicebox酷炫3d图片轮播切换❤️
  8. HTTPS加密传输过程
  9. Oracle DBA的SQL编写技能提升宝典(含SQL资源)
  10. ipad上linux终端,如何使用iSH在iPad或iPhone上获取Linux Shell
  11. 导航条——flash导航条
  12. 22春天津大学《财务会计》在线作业2
  13. 未来计算机教师职业愿景展望,教师愿景与职业规划
  14. Js一句话实现打开QQ和客服聊天
  15. 抓取淘宝天猫的商品的促销价格
  16. 【数字IC/FPGA】电平同步、脉冲同步、边沿同步
  17. CIO40: 学习.遇见更优秀的自己
  18. 数学建模方法-灰色预测法
  19. python数字转大写字母_python变量名称如何转化为大写字母?
  20. pa服务器系统,常见问题

热门文章

  1. MYSQL 本地计算机上的MySQL服务启动后停止。某些服务在未由其他服务或程序使用时将自动停止
  2. CG 超写实眼球 maya 制作
  3. 服务端判断苹果内容是否支付完成(二)
  4. 使用librosa对音频进行加载拼接截取叠加等操作
  5. Openstack(T版)私有云平台<环境部署>及安装<keystone组件>
  6. poi 解析中文_百度地图周边最近的POI查询并且解析出中文地址
  7. 麦克纳姆轮小车在gazebo中的实现
  8. C语言基础之clion的入门使用、数据类型基础
  9. C语言 游戏 俄罗斯方块 最全代码 c入门必学
  10. FFplay文档解读-21-音频过滤器六