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. 如何测量情感和感觉(以及它们之间的区别)?
  2. Extranet MPLS ×××
  3. Python常见的内置函数
  4. 近两年火热的微服务springboot不同配置文件详细讲解
  5. 去掉(不显示)关闭QQ游戏后跳出的广告
  6. VS 2013 Preview 自定义 SharePoint 2013 列表 之 两个Bug
  7. 第14章:傅里叶变换
  8. (转)Some awareness before migrating from x86 to x64
  9. 前端架构设计1:代码核心
  10. springcloud 服务网关Zuul实战(一)基本路由配置
  11. 1.5W 字搞懂 Spring Cloud,太牛了!
  12. 学计算机的大学生买什么U盘,大学生最容易丢的几样东西,最后一件最让人着急,网友:真实了...
  13. 小白量化《穿云箭集群量化》(3)量化策略编写(2)
  14. html a标签链接 点击下载文件
  15. 2021年Qt路线图
  16. 如何检查您附近(或任何地方)的空气质量
  17. Unity3D插件 Doozy UI 学习(三):UI Element
  18. 什么是 PDF 扁平化?怎样扁平化 PDF? 一起涨知识!
  19. Composite UI Application Block (CAB) 详解
  20. 计算机主机无法开机故障原因,惠普电脑开不了机怎么办 惠普电脑开不了机是什么原因 惠普电脑开机常见问题...

热门文章

  1. vue-router报Uncaught (in promise) Error: Redirected when going from “XX“ to “XX“,并且无法正常进入路由
  2. 漫画 | Code Review的巅峰
  3. 手机关键器件质量管理系列讲座(二):PCB十大质量问题与对策
  4. emqttd java 即时通讯_使用Emqttd搭建一个聊天室
  5. Java爬虫(四)通过屏幕录制顺序实现目标输入和点击
  6. (C语言最详细解析)1003 我要通过! (20 分)
  7. 每日一题 -- pta 基础编程题 超速判断
  8. 简易的获取今天的时间
  9. NIO/ZeroCopy
  10. python 角点检测