转自:http://blog.csdn.net/sgbfblog/article/details/7772153

贴上原文地址,好不容易找到了:brk(), sbrk() -- 改变数据段长度

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

[cpp] view plaincopy
  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 结束那里:

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

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

引用
[beyes@localhost C]./sbrkendofbsssection:0x8049938pmem:0x82ec0081−gapbetweenheapandbss:2762448pmem:0x82ec0082−gapbetweenheapandbss:2762448[beyes@localhostC]./sbrkendofbsssection:0x8049938pmem:0x82ec0081−gapbetweenheapandbss:2762448pmem:0x82ec0082−gapbetweenheapandbss:2762448[beyes@localhostC] ./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 的位置。那么根据这一点,检查一下每次在程序加载时,系统给堆的分配是不是等同大小的:

[cpp] view plaincopy
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <stdlib.h>
  4. #include <sys/time.h>
  5. #include <sys/resource.h>
  6. int main(void)
  7. {
  8. void *tret;
  9. char *pmem;
  10. pmem = (char *)malloc(32);
  11. if (pmem == NULL) {
  12. perror("malloc");
  13. exit (EXIT_FAILURE);
  14. }
  15. printf ("pmem:%p\n", pmem);
  16. tret = sbrk(0);
  17. if (tret != (void *)-1)
  18. printf ("heap size on each load: %lu\n", (long)tret - (long)pmem);
  19. return 0;
  20. }

运行上面的程序 3 次:

引用
[beyes@localhost C]./sbrkpmem:0x80c9008heapsizeoneachload:135160[beyes@localhostC]./sbrkpmem:0x80c9008heapsizeoneachload:135160[beyes@localhostC] ./sbrk 
pmem:0x9682008
heap size on each load: 135160
[beyes@localhost C]./sbrkpmem:0x9a7d008heapsizeoneachload:135160[beyes@localhostC]./sbrkpmem:0x9a7d008heapsizeoneachload:135160[beyes@localhostC] ./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]./sbrkpmem:0x804a008heapsizeoneachload:135160[beyes@localhostC]./sbrkpmem:0x804a008heapsizeoneachload:135160[beyes@localhostC] ./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 位置在堆的增长方向处:

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

运行输出:

引用
[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() 这个系统调用从映射区寻找可用的内存空间。

本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sky-heaven/p/6283441.html,如需转载请自行联系原作者

brk(), sbrk() 用法详解【转】相关推荐

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

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

  2. 强大的strace命令用法详解

    强大的strace命令用法详解_Linux教程_Linux公社-Linux系统门户网站 strace是什么? 按照strace官网的描述, strace是一个可用于诊断.调试和教学的Linux用户空间 ...

  3. python argv 详解_Python3 sys.argv[ ]用法详解

    sys.argv[]说白了就是一个从程序外部获取参数的桥梁,这个"外部"很关键,因为我们从外部取得的参数可以是多个,所以获得的是一个列表(list),也就是说sys.argv其实可 ...

  4. oracle中的exists 和 not exists 用法详解

    from:http://blog.sina.com.cn/s/blog_601d1ce30100cyrb.html oracle中的exists 和 not exists 用法详解 (2009-05- ...

  5. ROW_NUMBER() OVER()函数用法详解 (分组排序 例子多)

    ROW_NUMBER() OVER()函数用法详解 (分组排序 例子多) https://blog.csdn.net/qq_25221835/article/details/82762416 post ...

  6. python的继承用法_【后端开发】python中继承有什么用法?python继承的用法详解

    本篇文章给大家带来的内容是关于python中继承有什么用法?python继承的用法详解,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 面向对象三大特征 1.封装:根据职责将属性和方法 ...

  7. C++中substr()函数用法详解

    C++中substr()函数用法详解 原型: string substr (size_t pos = 0, size_t len = npos) const; 返回一个新构造的string对象,其值初 ...

  8. php theme_path,PHP_Yii2主题(Theme)用法详解,本文实例讲述了Yii2主题(Theme) - phpStudy

    Yii2主题(Theme)用法详解 本文实例讲述了Yii2主题(Theme)用法.分享给大家供大家参考,具体如下: 首先看看主要的配置方式: 'components' => [ 'view' = ...

  9. LayoutInflater的inflate函数用法详解

    LayoutInflater的inflate函数用法详解 LayoutInflater作用是将layout的xml布局文件实例化为View类对象. 获取LayoutInflater的方法有如下三种: ...

最新文章

  1. centos7快速搭建LAMP
  2. 【金三银四跳槽季】Java工程师的面试之路,需要“解锁”哪些技术盲点?
  3. IDEA_Spring Data JPA有关报错Cannot resolve table 'XXX'
  4. [WPF系列]Button 自定义
  5. Ubuntu下安装visual studio code
  6. 复杂的权限按钮控制优化
  7. [bzoj] 1257 余数之和sum || 数论
  8. Android学习笔记----SQLiteDatabase 自带添加、删除、更新、查询的操作方法:实现添加,删除,更新,查询,和分页,统计
  9. 单机版kubernetes1.13安装
  10. IDEA 导入cordova3.5工程目录注意事项
  11. 在 mac 上用海盗船键盘
  12. 英语发音规则---/ŋ/与/ŋg/的读音区别
  13. 核爆rpg学院站计算机,给新人的一点收集建议
  14. 使用mindspore过程中Using shared memory queue, but rowsize is larger than allocated
  15. Windows10使用命令行打开3389_内网渗透(实验)之域渗透深入弹shell开3389拿域控
  16. 微信小程序制作——获取用户信息
  17. 基于QWebView开发的浏览器通过需要证书认证网站的方法
  18. vb 复制 剪贴板 html,用vb实现将脚本的输出复制到剪贴板
  19. 鲁智深吃馒头,约瑟夫环问题
  20. pandas_datareader下载雅虎财经股价数据

热门文章

  1. 工作中最常用的Excel函数公式大全
  2. 去掉easyui datagrid内部虚线的方式。
  3. ECMall如何在后台添加模板编辑页
  4. HBA driver for linux
  5. Ubuntu 9.04下让Swing和Swt编写的Java桌面程序运行
  6. 18个堪称神器的命令行工具,高效运维必备
  7. pygame开发PC端微信打飞机游戏
  8. 计算机硬件存储器,个人计算机的存储器系统 说说内核与计算机硬件结构(5)
  9. 数据库与数据库管理系统的关系
  10. Netty与Spring WebSocket