brk(), sbrk() 用法详解

2023-10-29

http://blog.csdn.net/ssjhust123/article/details/7772153

brk() , sbrk() 的声明如下:

  1. #include <unistd.h>  
  2. int brk(void *addr);  
  3. void *sbrk(intptr_t increment);  

这两个函数都用来改变 "program break" (程序间断点)的位置,这个位置可参考下图:

如 man 里说的:

引用
brk()  and  sbrk() change the location of the program break, which defines the end of the process's data segment (i.e., the program break is the first location after the end of the uninitialized data segment). 
brk() 和 sbrk() 改变 "program brek" 的位置,这个位置定义了进程数据段的终止处(也就是说,program break 是在未初始化数据段终止处后的第一个位置)。
如此翻译过来,似乎会让人认为这个 program break 是和上图中矛盾的,上图中的 program break 是在堆的增长方向的第一个位置处(堆和栈的增长方向是相对的),而按照说明手册来理解,似乎是在 bss segment 结束那里(因为未初始化数据段一般认为是 bss segment)。


首先说明一点,一个程序一旦编译好后,text segment ,data segment 和 bss segment 是确定下来的,这也可以通过 objdump 观察到。下面通过一个程序来测试这个 program break 是不是在 bss segment 结束那里:

  1. #include <stdio.h>  
  2. #include <unistd.h>  
  3. #include <stdlib.h>  
  4. #include <sys/time.h>  
  5. #include <sys/resource.h>  
  6.    
  7.    
  8. int bssvar;    //声明一个味定义的变量,它会放在 bss segment 中  
  9.    
  10.    
  11. int main(void)  
  12. {  
  13.     char *pmem;  
  14.     long heap_gap_bss;  
  15.    
  16.    
  17.     printf ("end of bss section:%p\n", (long)&bssvar + 4);  
  18.    
  19.    
  20.     pmem = (char *)malloc(32);          //从堆中分配一块内存区,一般从堆的开始处获取  
  21.     if (pmem == NULL) {  
  22.         perror("malloc");  
  23.         exit (EXIT_FAILURE);  
  24.     }  
  25.    
  26.    
  27.     printf ("pmem:%p\n", pmem);  
  28.    
  29.    
  30. //计算堆的开始地址和 bss segment 结束处得空隙大小,注意每次加载程序时这个空隙都是变化的,但是在同一次加载中它不会改变  
  31.     heap_gap_bss = (long)pmem - (long)&bssvar - 4;            
  32.     printf ("1-gap between heap and bss:%lu\n", heap_gap_bss);  
  33.    
  34.    
  35.     free (pmem);   //释放内存,归还给堆  
  36.        
  37.     sbrk(32);        //调整 program break 位置(假设现在不知道这个位置在堆头还是堆尾)  
  38.      pmem = (char *)malloc(32);   //再一次获取内存区  
  39.         if (pmem == NULL) {  
  40.                 perror("malloc");  
  41.                 exit (EXIT_FAILURE);  
  42.         }  
  43.    
  44.    
  45.         printf ("pmem:%p\n", pmem);   //检查和第一次获取的内存区的起始地址是否一样  
  46.     heap_gap_bss = (long)pmem - (long)&bssvar - 4;  //计算调整 program break 后的空隙  
  47.     printf ("2-gap between heap and bss:%lu\n", heap_gap_bss);  
  48.    
  49.    
  50.     free(pmem);   //释放  
  51.     return 0;  
  52. }  

下面,我们分别运行两次程序,并查看其输出:


引用
[beyes@localhost C]$ ./sbrk  
end of bss section:0x8049938
pmem:0x82ec008
1-gap between heap and bss: 2762448
pmem:0x82ec008
2-gap between heap and bss: 2762448
[beyes@localhost C]$ ./sbrk  
end of bss section:0x8049938
pmem:0x8dbc008
1-gap between heap and bss: 14100176
pmem:0x8dbc008
2-gap between heap and bss: 14100176


从上面的输出中,可以发现几点:
1. bss 段一旦在在程序编译好后,它的地址就已经规定下来。
2. 一般及简单的情况下,使用 malloc() 申请的内存,释放后,仍然归还回原处,再次申请同样大小的内存区时,还是从第 1 次那里获得。
3. bss segment 结束处和堆的开始处的空隙大小,并不因为 sbrk() 的调整而改变,也就是说明了 program break 不是调整堆头部。

所以,man 手册里所说的  “program break 是在未初始化数据段终止处后的第一个位置” ,不能将这个位置理解为堆头部。这时,可以猜想应该是在堆尾部,也就是堆增长方向的最前方。下面用程序进行检验:

当 sbrk() 中的参数为 0 时,我们可以找到 program break 的位置。那么根据这一点,检查一下每次在程序加载时,系统给堆的分配是不是等同大小的:
  1. #include <stdio.h>  
  2. #include <unistd.h>  
  3. #include <stdlib.h>  
  4. #include <sys/time.h>  
  5. #include <sys/resource.h>  
  6.    
  7.    
  8. int main(void)  
  9. {  
  10.         void *tret;  
  11.         char *pmem;  
  12.    
  13.    
  14.    
  15.         pmem = (char *)malloc(32);  
  16.         if (pmem == NULL) {  
  17.                 perror("malloc");  
  18.                 exit (EXIT_FAILURE);  
  19.         }  
  20.    
  21.    
  22.         printf ("pmem:%p\n", pmem);  
  23.    
  24.         tret = sbrk(0);  
  25.         if (tret != (void *)-1)  
  26.                 printf ("heap size on each load: %lu\n", (long)tret - (long)pmem);  
  27.    
  28.    
  29.     return 0;  
  30. }  

运行上面的程序 3 次:
引用
[beyes@localhost C]$ ./sbrk  
pmem:0x80c9008
heap size on each load: 135160
[beyes@localhost C]$ ./sbrk  
pmem:0x9682008
heap size on each load: 135160
[beyes@localhost C]$ ./sbrk  
pmem:0x9a7d008
heap size on each load: 135160
[beyes@localhost C]$ ./sbrk  
pmem:0x8d92008
heap size on each load: 135160
[beyes@localhost C]$ vi sbrk.c
从输出可以看到,虽然堆的头部地址在每次程序加载后都不一样,但是每次加载后,堆的大小默认分配是一致的。但是这不是不能改的,可以使用 sysctl 命令修改一下内核参数:
引用
#sysctl -w kernel/randomize_va_space=0
这么做之后,再运行 3 次这个程序看看:
引用
[beyes@localhost C]$ ./sbrk  
pmem:0x804a008
heap size on each load: 135160
[beyes@localhost C]$ ./sbrk  
pmem:0x804a008
heap size on each load: 135160
[beyes@localhost C]$ ./sbrk  
pmem:0x804a008
heap size on each load: 135160
从输出看到,每次加载后,堆头部的其实地址都一样了。但我们不需要这么做,每次堆都一样,容易带来缓冲区溢出攻击(以前老的 linux 内核就是特定地址加载的),所以还是需要保持 randomize_va_space 这个内核变量值为 1 。

下面就来验证 sbrk() 改变的 program break 位置在堆的增长方向处:
  1. #include <stdio.h>  
  2. #include <unistd.h>  
  3. #include <stdlib.h>  
  4. #include <sys/time.h>  
  5. #include <sys/resource.h>  
  6.    
  7.    
  8. int main(void)  
  9. {  
  10.         void *tret;  
  11.         char *pmem;  
  12.         int i;  
  13.         long sbrkret;  
  14.    
  15.        pmem = (char *)malloc(32);  
  16.         if (pmem == NULL) {  
  17.                 perror("malloc");  
  18.                 exit (EXIT_FAILURE);  
  19.         }  
  20.    
  21.    
  22.         printf ("pmem:%p\n", pmem);  
  23.    
  24.          for (i = 0; i < 65; i++) {  
  25.                 sbrk(1);  
  26.                 printf ("%d\n", sbrk(0) - (long)pmem - 0x20ff8);   //0x20ff8 就是堆和 bss段 之间的空隙常数;改变后要用 sbrk(0) 再次获取更新后的program break位置  
  27.         }  
  28.        free(pmem);  
  29.    
  30.           
  31.        return 0;  
  32. }  

运行输出:
引用
[beyes@localhost C]$ ./sbrk  
pmem:0x804a008
1
2
3
4
5

... ...
61
62
63
64

从输出看到,sbrk(1) 每次让堆往栈的方向增加 1 个字节的大小空间。

而 brk() 这个函数的参数是一个地址,假如你已经知道了堆的起始地址,还有堆的大小,那么你就可以据此修改 brk() 中的地址参数已达到调整堆的目的。

实际上,在应用程序中,基本不直接使用这两个函数,取而代之的是 malloc() 一类函数,这一类库函数的执行效率会更高。 还需要注意一点,当使用 malloc() 分配过大的空间,比如超出 0x20ff8 这个常数(在我的系统(Fedora15)上是这样,别的系统可能会有变)时,malloc 不再从堆中分配空间,而是使用 mmap() 这个系统调用从映射区寻找可用的内存空间。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

brk(), sbrk() 用法详解 的相关文章

  • 掌握 Linux 调试技术

    http www ibm com developerworks cn linux sdk l debug index html ibm pcon 在 Linux 上找出并解决程序错误的主要方法 Steve Best sbest us ibm
  • Linux文件编程常用函数详解——wait()函数

    函数原型和头文件 include
  • Linux下gdb编译调试程序

    Linux下gdb编译调试程序 前言 一 调试前的准备 二 gdb中断点的使用 三 gdb中运行调试程序 四 gdb中打印值和监控值 总结 参考 前言 本文记录调试工具gdb的一些基础使用方式 gdb是一个程序调试工具 注意 如果是程序语法
  • Unix环境编程中的apue.h和err_quit、err_sys问题

    现在开始学习 Unix环境编程 这本书 在学习的时候总会遇到书中的程序编译时会出现问题 所以自己将遇到的问题及解决的方法进行总结 找不到头文件apue h和err qiut err sys 1 apue h是作者自定义的一个头文件 并不是U
  • gdb调试正在运行的进程

    http elisawell blog 163 com blog static 171462881201223061149953 有时会遇到一种很特殊的调试需求 对当前正在运行的其它进程进行调试 正是我今天遇到的情形 这种情况有可能发生在那
  • brk(), sbrk() 用法详解

    http blog csdn net ssjhust123 article details 7772153 brk sbrk 的声明如下 cpp view plain copy include
  • 检验IP地址有效性

    使用inet aton函数
  • Linux中修改MAC地址和IP地址的命令(netset、ip、ifconfig)

    修改MAC地址 ip link set eth0 address aa aa aa aa aa aa 解析 eth0是网卡的名字 可以用ifconfig命令查看 aa aa aa aa aa aa是想要修改后的MAC地址 修改IP地址 1
  • cd命令、pwd命令和环境变量PWD、OLDPWD的关联

    1 cd命令 cd命令这里不多介绍 cd 命令是返回上次所在的目录 2 PWD和OLDPWD环境变量 dai ubuntu env PWD home dai OLDPWD dai ubuntu 3 关联 1 当你输入 cd 命令返回上次的目
  • PF_NETLINK应用实例NETLINK_KOBJECT_UEVENT具体实现--udev实现原理

    相对于linux来说 udev还是一个新事物 然而 尽管它03年才出现 尽管它很低调 J 但它无疑已经成为linux下不可或缺的组件了 udev是什么 它是如何实现的 最近研究Linux设备管理时 花了一些时间去研究udev的实现 udev
  • 使用Crash工具分析 Linux dump文件

    前言 Linux 内核 以下简称内核 是一个不与特定进程相关的功能集合 内核的代码很难轻易的在调试器中执行和跟踪 开发者认为 内核如果发生了错误 就不应该继续运 行 因此内核发生错误时 它的行为通常被设定为系统崩溃 机器重启 基于动态存储器
  • linux glob函数man页与实例

    Linux Programmer s Manual NAME glob globfree find pathnames matching a pattern free memory from glob SYNOPSIS include
  • chmod函数

    int chmod const char pathname mode t mode 作用 修改文件的权限 参数 mode 需要修改的权限值 宏或者八进制数 返回值 成功 0 失败 1 include
  • CentOS 7升级gcc/CentOS 7 yum 安装gcc

    centos7自带的gcc版本是4 8 手动升级安装很锻炼 毕竟已经0202年了 devtoolset 7 Developer Toolset is designed for developers working on CentOS or
  • Linux system函数返回值

    http blog cheyo net p 42 例 1 status system test sh 1 先统一两个说法 1 system返回值 指调用system函数后的返回值 比如上例中status为system返回值 2 shell返
  • gethostbyname() -- 用域名或主机名获取IP地址

    http hi baidu com zengzhaonong item 87d9d296d0824cbb82d29570 include
  • 如何快速确定程序的入口

    前言 在阅读代码时 知道程序的入口十分重要 这有助于快速理清程序的逻辑框架 我们找到程序入口后 顺着代码的执行顺序来阅读代码 可以比较容易的理解代码 这里说的代码是编译后成为可执行程序的代码 在linux中就是elf格式 被编译成可执行程序
  • TCP/IP编程实现远程文件传输

    TCP IP编程实现远程文件传输 在TCP IP网络结构中 为了保证网络安全 网络人员往往需要在路由器上添加防火墙 禁止非法用户用ftp等安全危害较大的TCP IP协议访问主机 而有时系统维护人员需要用ftp将一些文件从中心机房主机传到前端
  • [Ubuntu]GTest安装和测试

    1 Ubuntu直接通过控制台安装 sudo apt get install libgtest dev 2 编译链接库 2 1进入gtest文件夹 cd usr src gtest 2 2编译 没有安装Cmake的请先安装cmake sud
  • 主线程退出后,子线程会不会退出

    额 好吧 这是个标题党 其实所有的线程都是平级的 根本不存在主线程和子线程 下文所述为了方便 将在main函数中的线程看做主线程 其它线程看成子线程 特此说明 先考虑以下代码 include

随机推荐

  • 连接器出线方法分享(持续更新)

  • 基于情境化时空网络的出租车OD需求预测

    1 文章信息 Contextualized Spatial Temporal Network for Taxi Origin Destination Demand Prediction 是2019年发表在IEEE上的一篇文章 2 摘要 本文
  • 区块链之添加节点

    1 查询节点信息 gt admin nodeInfo enode enode b817560f061b1f14551f87060806847c4c6b7cf8b56b6027fd3d8400c3abb4e2a3d535dd78ab46f28
  • CAD打开字体无法选择,cad打开无字体,cad无法加载字体

    在命令栏中输入filedia 然后回车 输入1保存关闭 再重新打开即可
  • gcc编译出现:error: invalid operands to binary & (have ‘char *’ and ‘int *’)

    1 2 gt File Name ptr variable c 3 gt Author Mr Yang 4 gt Purpose 演示指向变量的指针 5 gt Created Time 2017年06月03日 星期六 08时47分33秒 6
  • JDBC PostgreSQL

    上一节 JDBC可以操作多种数据库 而且都是标准化操作 区别仅仅在使用不同的数据库连接驱动程序 及URL连接方式的书写 引用SQL包 import java sql public class JDBCTest param args publ
  • 关于RocketMq消息积压问题排查

    1 最近生产的mq出现了一个问题 我的消费者是集群 就是双节点 现在消息积压到1亿多条 如下图所示 其中有两个问题 问题1 就是为什么我的消息积压这么多 问题2 我的消费者是集群 为什么只有一台消息在消费 2 接着开始排查问题 结果发现在消
  • conda进行transformers安装

    首先建立新环境 conda create n myenv python 3 8 安装numpy和pytorch conda install numpy conda install pytorch torchvision torchaudio
  • 人脸检测 + 数据训练 + 人脸识别

    准备工作 安装opencv pip install opencv 安装opencv contrib pip install opencv contrib 创建文件夹 文件结构为 一 编写一个基础的人脸识别 import cv2 as cv
  • 服务器存档里怎么修改器,云服务器存档修改器

    云服务器存档修改器 内容精选 换一换 修改云服务器信息 目前支持修改云服务器名称及描述和hostname 该接口支持企业项目细粒度权限的校验 具体细粒度请参见 ecs cloudServers put云服务器hostname修改后 需要重启
  • 面试官常问到的问题

    dom是什么 虚拟DOM又是什么 有何作用 谈谈你对vue的理解以及2和3的区别 双向数据绑定 数据类型有哪些 深拷贝和浅拷贝是什么 它们的区别是什么 组建通信了解吗 重绘和回流是什么 一般开发用什么布局 VUEX了解吗 说说它的五大属性及
  • react-native 屏幕尺寸和文字大小适配

    转载存档 现在的手机品牌和型号越来越多 导致我们平时写布局的时候会在个不同的移动设备上显示的效果不同 比如我们的设计稿一个View的大小是300px 如果直接写300px 可能在当前设备显示正常 但到了其他设备可能就会偏小或者偏大 这就需要
  • yum私有仓库的实现

    目录 一 yum私有仓库的实现 二 画图 TCP协议和三次握手及四次挥手 三 centos ubuntu 静态网卡IP配置 四 实现免密登录脚本 expect登录远程主机 将生成的密钥写入到目标主机 expect测试远程登录 一 yum私有
  • GB2312编码理解

    计算机信息交换使用汉字编码字符集是国家1980年发布 标准号为 GB2312 1980 GB2312编码主要适用于汉字处理 汉字通讯等系统之间的信息交换 基本集主要收集了6763个汉字和682个图形字符 整个字符集分成94个区 每个区有94
  • 坐标变换(1)—向量和坐标系

    1 标量 在介绍向量之前 有必要介绍一下标量 scalar 标量是一个数字 只有大小 没有方向 不过有正负 例如温度 重量等 2 向量 向量 vector 是多个数字组成的列表 n n n个有次序的数 x 1
  • [授权发表]Shell编程范例之文件操作

    by falcon 最初发表 泰晓科技 聚焦嵌入式 Linux 追本溯源 见微知著 原文链接 Shell编程范例之文件操作 评论说明 为更好地聚合大家的讨论 请到上面原文的评论区回复 前言 这一周我们来探讨文件操作 在日常学习和工作中 我们
  • 计算机网络笔记四(应用层:DNS协议、FTP协议)

    1 DNS协议 1 1DNS协议基本概念 1 1 1什么是域名 域名 英语 Domain Name 又称网域 是由一串用点分隔的名字组成的Internet上某一台计算机或计算机组的名称 用于在数据传输时对计算机的定位标识 有时也指地理位置
  • 逻辑器件1

    1 COMS器件 常用的74LV 74AC 74AHC 74HC等系列都属于CMOS器件 输入端是上P下N MOS组成的电路 2 缺点是高低电平之间有一段非稳态区间 这个区间PMOS和NMOS部分导通 输出会有振荡 3 逻辑器件 TTL C
  • Windows环境下pcl点云库 安装配置全流程(精简、有效)

    版权声明 本文为博主原创文章 遵循 CC 4 0 BY SA 版权协议 转载请附上原文出处链接和本声明 本文链接 https blog csdn net zaibeijixing article details 130770476 本文为W
  • brk(), sbrk() 用法详解

    http blog csdn net ssjhust123 article details 7772153 brk sbrk 的声明如下 cpp view plain copy include