brk() , sbrk() (转自CSDN)

原文链接:https://blog.csdn.net/heybeaman/article/details/80348582

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

#include<unistd.h>

intbrk(void*addr);

void*sbrk(intptr_tincrement);

这两个函数都用来改变 "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 结束那里:

#include<stdio.h>

#include<unistd.h>

#include<stdlib.h>

#include<sys/time.h>

#include<sys/resource.h>

intbssvar;//声明一个味定义的变量,它会放在 bss segment 中

intmain(void)

{

char*pmem;

longheap_gap_bss;

printf("end of bss section:%p\n", (long)&bssvar +4);

pmem = (char*)malloc(32);//从堆中分配一块内存区,一般从堆的开始处获取

if(pmem ==NULL) {

perror("malloc");

exit(EXIT_FAILURE);

    }

printf("pmem:%p\n", pmem);

//计算堆的开始地址和 bss segment 结束处得空隙大小,注意每次加载程序时这个空隙都是变化的,但是在同一次加载中它不会改变

heap_gap_bss = (long)pmem - (long)&bssvar -4;

printf("1-gap between heap and bss:%lu\n", heap_gap_bss);

free(pmem);//释放内存,归还给堆


sbrk(32);//调整 program break 位置(假设现在不知道这个位置在堆头还是堆尾)

pmem = (char*)malloc(32);//再一次获取内存区

if(pmem ==NULL) {

perror("malloc");

exit(EXIT_FAILURE);

        }

printf("pmem:%p\n", pmem);//检查和第一次获取的内存区的起始地址是否一样

heap_gap_bss = (long)pmem - (long)&bssvar -4;//计算调整 program break 后的空隙

printf("2-gap between heap and bss:%lu\n", heap_gap_bss);

free(pmem);//释放

return0;

}

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

引用

[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 的位置。那么根据这一点,检查一下每次在程序加载时,系统给堆的分配是不是等同大小的:

#include<stdio.h>

#include<unistd.h>

#include<stdlib.h>

#include<sys/time.h>

#include<sys/resource.h>

intmain(void)

{

void*tret;

char*pmem;

pmem = (char*)malloc(32);

if(pmem ==NULL) {

perror("malloc");

exit(EXIT_FAILURE);

        }

printf("pmem:%p\n", pmem);

tret = sbrk(0);

if(tret != (void*)-1)

printf("heap size on each load: %lu\n", (long)tret - (long)pmem);

return0;

}

运行上面的程序 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 位置在堆的增长方向处:

#include<stdio.h>

#include<unistd.h>

#include<stdlib.h>

#include<sys/time.h>

#include<sys/resource.h>

intmain(void)

{

void*tret;

char*pmem;

inti;

longsbrkret;

pmem = (char*)malloc(32);

if(pmem ==NULL) {

perror("malloc");

exit(EXIT_FAILURE);

        }

printf("pmem:%p\n", pmem);

for(i =0; i <65; i++) {

sbrk(1);

printf("%d\n", sbrk(0) - (long)pmem -0x20ff8);//0x20ff8 就是堆和 bss段 之间的空隙常数;改变后要用 sbrk(0) 再次获取更新后的program break位置

        }

free(pmem);


return0;

}

运行输出:

引用

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容

  • 在C语言中,五种基本数据类型存储空间长度的排列顺序是: A)char B)char=int<=float C)ch...
    夏天再来阅读 3,076评论 0 2
  • 第1章 第一个C程序第2章 C语言基础第3章 变量和数据类型第4章 顺序结构程序设计第5章 条件结构程序设计第6章...
    小狮子365阅读 10,239评论 3 71
  • 坚信知组7号彭克军日精进打卡201800503 一、要谦虚不要骄傲 1.警惕固定费用的增加、首先让全公司的人养成节...
    彭克军阅读 149评论 0 0
  • 赵四小姐,名赵一荻,又名绮霞,出生于香港。赵四小姐出生的时候,东方天际出现一片绮丽多彩的霞光,因此而得名。在家中排...
    生白术阅读 193评论 0 0
  • 乐极生悲,喜极而泣,大多数时候也是物极必反的结果。为什么人在这种环境之下能够表现出截然不同的态度和情感?也许是因为...
    527296628fa8阅读 322评论 0 3