https://makelinux.github.io/kernel/map/
内存是啥?下图就是:
sys_brk
内存管理代码在内核源码“/mm”下。
/* Below are the C functions used to declare the raw syscalls. They try to be * architecture-agnostic, and return either a success or -errno. Declaring them * static will lead to them being inlined in most cases, but it's still possible * to reference them by a pointer if needed. */static __attribute__((unused))void *sys_brk(void *addr){ return (void *)my_syscall1(__NR_brk, addr);}
首先看一下用户态的brk函数:
#include
int brk(void *addr);
void *sbrk(intptr_t increment);
为了能够很好的理解这两个函数,我们需要先来理解一下uc程序的内存模型(需要有一定的虚拟内存的概念才能很好的理解!)32位的操作系统,会有4G的虚拟地址空间。
1、程序代码区:存放函数体的二进制代码。
2、全局区数据区:全局数据区划分为三个区域。全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。常量数据存放在另一个区域里。这些数据在程序结束后由系统释放。我们所说的BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称。
3、堆区:一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
4、栈区:由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
5、命令行参数区:存放命令行参数和环境变量的值。
#include #include #include #include #include //声明一个味定义的变量,它会放在 bss segment 中int bssvar; int main(void) { char *pmem; long heap_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); //调整 program break 位置(假设现在不知道这个位置在堆头还是堆尾) sbrk(32); //再一次获取内存区 pmem = (char *)malloc(32); if (pmem == NULL) { perror("malloc"); exit (EXIT_FAILURE); } //检查和第一次获取的内存区的起始地址是否一样 printf ("pmem:%p\n", pmem); //计算调整 program break 后的空隙 heap_gap_bss = (long)pmem - (long)&bssvar - 4; printf ("2-gap between heap and bss:%lu\n", heap_gap_bss); //释放 free(pmem); return 0; }
运行结果:
$ ./a.exeend of bss section:0x1004071a4pmem:0x6000005301-gap between heap and bss:21470614412pmem:0x6000005302-gap between heap and bss:21470614412
从上面的输出中,可以发现几点:
- 1. bss 段一旦在在程序编译好后,它的地址就已经规定下来。
- 2. 一般及简单的情况下,使用 malloc() 申请的内存,释放后,仍然归还回原处,再次申请同样大小的内存区时,还是从第 1 次那里获得。
- 3. bss segment 结束处和堆的开始处的空隙大小,并不因为 sbrk() 的调整而改变,也就是说明了 program break 不是调整堆头部。
所以,man 手册里所说的 “program break 是在未初始化数据段终止处后的第一个位置” ,不能将这个位置理解为堆头部。这时,可以猜想应该是在堆尾部,也就是堆增长方向的最前方。下面用程序进行检验:
当 sbrk() 中的参数为 0 时,我们可以找到 program break 的位置。那么根据这一点,检查一下每次在程序加载时,系统给堆的分配是不是等同大小的:#include #include #include #include #include int main(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); return 0;}
运行结果:
$ ./a.exepmem:0x6000004e0heap size on each load: 326432
这么做之后,再运行 3 次这个程序看看:
$ ./a.exepmem:0x6000004e0heap size on each load: 326432Toa@DESKTOP-ASO4FBT /cygdrive/g/test/c/glibc/unistd$ ./a.exepmem:0x6000004e0heap size on each load: 326432Toa@DESKTOP-ASO4FBT /cygdrive/g/test/c/glibc/unistd$ ./a.exepmem:0x6000004e0heap size on each load: 326432
从输出看到,每次加载后,堆头部的其实地址都一样了。但我们不需要这么做,每次堆都一样,容易带来缓冲区溢出攻击(以前老的 linux 内核就是特定地址加载的),所以还是需要保持 randomize_va_space 这个内核变量值为 1 。
下面就来验证 sbrk() 改变的 program break 位置在堆的增长方向处:
#include #include #include #include #include int main(void){ void *tret; char *pmem; int i; long sbrkret; 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); //0x20ff8 就是堆和 bss段 之间的空隙常数; //改变后要用 sbrk(0) 再次获取更新后的program break位置 printf ("%d\n", sbrk(0) - (long)pmem - 0x20ff8); } free(pmem); return 0;}
从输出看到,sbrk(1) 每次让堆往栈的方向增加 1 个字节的大小空间。
而 brk() 这个函数的参数是一个地址,假如你已经知道了堆的起始地址,还有堆的大小,那么你就可以据此修改 brk() 中的地址参数已达到调整堆的目的。
实际上,在应用程序中,基本不直接使用这两个函数,取而代之的是 malloc() 一类函数,这一类库函数的执行效率会更高。还需要注意一点,当使用 malloc() 分配过大的空间,比如超出 0x20ff8 这个常数(在我的系统(Fedora15)上是这样,别的系统可能会有变)时,malloc 不再从堆中分配空间,而是使用 mmap() 这个系统调用从映射区寻找可用的内存空间。
《 C语言内存分配模型->brk() sbrk() 》
https://blog.csdn.net/hipilee/article/details/8075251
--推荐阅读--
《Slab allocators in the Linux Kernel: SLAB, SLOB, SLUB》