您的位置:首页 > 运维架构 > Linux

对程序内存结构和程序存储位置的理解

2018-02-02 00:00 369 查看

对程序内存结构和程序存储位置的理解

之前看过许多有关系统中,对内存分配的一些文章,一直一知半解,直到调研一个系统调用,才真正的弄明白是怎么回事. 有一个重点就是,这里说的全局数据区,全局,说的不说c++中的全局的概念,如果那样理解很容易感觉模棱两可,这里说的全局说 系统看来,不是一个程序的看待,对系统来说,一个程序的全局就是,需要在程序运行前准备好的内存,对于c++中的变量,系统看来只有三种:全局,stack,bss, 其他两个,stack管程序调用,bss指需要,但是运行的时候才能知道内容. 加载慢的一个优化手段就是,调整程序,让bss代替data,具体为什么,下文会说,

这个内容想写写来源于对一个系统调用的研究 brk(void* addr), 某些程序加载的时候cpu很高,使用strace -p PID查看以后,发现他 一直执行brk这个系统调用.

brk: 调整程序的数据段,把数据段的地址往高地址调整,,,,,这个有啥用?地址段不是应该加载的时候分配好的吗?有的说这个还能分配动态内存,动态内存不是在heep区分配的吗?总之疑点重重.

一般系统中的地址分配都是从低到高, 依次是:

text段 => 存放指令代码,在可执行文件中也由这一部分,系统会把文件中的text端拷贝到内存里

bss段 => 未初始化的全局数据区,一般系统会弄成0,这一部分在可执行文件中是没有的,只是记录一下需要多少这种空间

data数据段 => 这一部分比较特殊,下文会具体说明,可执行文件中可能有也可能没有.

——-> 这有个指针(系统用的,不过用户态可以通过调用修改) _edata

heep堆 => 这是通常 认为 的malloc申请内存的地方,其实也不一定的,下文会说

stack栈地址 => 栈地址,通常的函数调式都会用到的,

==>

==>

3G以上就是内核区了,用户态就别想自由使用了.

brk的功能就是,把 _edata的地址网高出推,往 heap这边推,可以想象,brk把_data往heep推了,heep能分配的空间肯定就少了啊,

对的,是这样的,

有关brk分配内存的说明

c语言中分配内存,当然是使用malloc了,brk分配内存的功能,就是在这里的,glibc对malloc的实现,是如果分配的内存在 126k以内, 就使用brk分配,把brk往heep端推一点,多余出来的数据段,返回给用户,用作动态内存,当读写这个内存的时候,就会引发一个 缺页中断 让系统在物理内存里分配段内存. 126k以内都用brk,我们平时试试的基本都是使用brk分配的啊,我不信!!,我写个程序给你证明

#include<iostream>
#include<unistd.h>
#include<stdlib.h>
using namespace std;
int main(){
void*p = malloc(1);
cout<<p<<endl;
int a = brk((void*)((char*)p+1000));  //这句调步调影响下次malloc返回的地址
//int a = brk((void*)((char*)p+1));
cout<<a<<endl;
void* d=malloc(1);
cout<<d<<endl;
return 0;
}

一开始我使用 brk(p+1) 程序直接崩溃了,后来查了一下两次malloc的地址差,发现一次malloc 已经把_edata的地址上调了460了,如果我在上调,只能调这个数更大的范围了, 这里有个隐含规则 brk 的地址,只能往高处调,不能往低处调,因为已经调好的内存, 可能已经分配了,我们私自把地址往下调了,brk也返回成功0,可是程序会直接崩溃.

如果malloc申请的地址超过了126k,才会调用mmap到heap区去申请内存. brk和heap是linux系统调用,malloc是glibc的函数,不是系统调用.

如果_edata只能往上调,那麽,即使malloc把内存释放了,把_edata调下来,也只能依次释放的,可是用户程序不一定这样来,这不就是 浪费吗? 是的,是这样,用户free,这个地址的一段内存就不能回收了,只有当最高处的地址被free了, 才能把_edata调下来,同时通知系统,取消映射. malloc默认没有一个一个字节的向高地址加,而是一次就加许多,这个许多如何知道呢,这个和系统分配的页大小有关,查看办法如下: 使用getconf PAGESIZE 查看系统的页大小
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息