2015-11-10 18:15:44 liusirboke 阅读数 2595

1.linux应用程序内存布局

这里写图片描述

  • 从低地址到高地址分别为:代码段、数据段、BSS段、堆、栈
  • 对向高内存地址生长,栈想低内存地址生长
  • linux中所有的应用程序都是这个布局,每个应用程序都是从0x80480000这个地址开始的,这样冲突吗?不冲突,因为这个地址是一个虚拟地址,linux中每个应用程序都有自己的虚拟地址空间。

2.2.查看linux中应用程序内存布局

方 法:通过查看应用程序进程中的进程地址映射文件实现。

例如查看2053号进程的内存布局
命令如下:cat /proc/2053/maps
结果如下:
这里写图片描述

  • 上图中第一行是代码段,因为代码段可读可执行但不可写,从中也可以看出该程序的起始地址是0x80480000。

  • 第二行是数据段的内存地址范围。

  • 第三行是堆的内存地址范围。

  • 最后一行是栈的内存地址范围。

3.linux中变量所在内存位置分析

我们通过下面的程序来分析各种变量在程序中的内存位置。

文件名:addr.c

#include <stdio.h>
#include <stdlib.h>

int A;              //全局未初始化变量
int B = 2;          //全局初始化变量
int static C;       //全局静态未初始化变量
int static D = 4;   //全局静态初始化变量
int const E = 5;    //全局常量

int main()
{
    int *m_addr;

    int a;           //局部未初始化变量
    int b = 2;       //局部初始化变量
    int static c;    //局部静态未初始化变量
    int static d = 4;//局部静态初始化变量
    int const e =5;  //局部常量

    m_addr = malloc(sizeof(int));

    printf("A_global_addr = %0x\n",&A);
    printf("B_global_init_addr = %0x\n",&B);
    printf("C_global_static_addr = %0x\n",&C);
    printf("D_global_static_init_addr = %0x\n",&D);
    printf("E_global_const_addr = %0x\n",&E);

    printf("\n");

    printf("a_addr = %0x\n",&a);
    printf("b_init_addr = %0x\n",&b);
    printf("c_static_addr = %0x\n",&c);
    printf("d_static_initaddr = %0x\n",&d);
    printf("e_const_addr = %0x\n",&e);

    printf("m_addr = %p\n",m_addr);

    while(1);

    return 0;
}
  • 程序执行后的结果:

这里写图片描述

  • 查看该程序进程的maps得到该应用程序的内存分布如下

这里写图片描述

  • 通过以上两幅图片的比对得出:
代码段 E
数据段 A、B、C、D、c、d
m_addr
a、b、e

总结:各个段存放的数据类型

栈:局部变量(初始化或者未初始化)或者局部常量(const)
堆:动态分配空间(malloc)
数据段:全局变量(初始化或者未初始化),静态变量(static)
代码段:全局常量(const)

4. 关于bss段

  • 使用readelf工具查看该段地址空间。
  • readelf -S (可执行程序名)
    例如该应用程序:readelf -S addr
    查看结果:
    这里写图片描述

第25号,就是bss段的内存地址范围0x080498c0~0x080498d4(其空间大小为14B)

bss段:A、C、c

总结:
bss段存放的是未初始化的数据(变量),只要是存放在数据段(大概念)又没有初始化的就存在这里。

2014-04-20 20:59:40 cyp331203 阅读数 877


先来看一段简单代码:

#include <stdio.h>
#include <unistd.h>

int main()
{
    printf("%d\n",getpid());
    while(1);
}

运行结果:

这时另开一个终端,输入cat /proc/10073/maps,出现如下显示:

08048000-08049000 r-xp 00000000 fd:01 14844      /home/Alex/DaNei/Interview/a.out
08049000-0804a000 r--p 00000000 fd:01 14844      /home/Alex/DaNei/Interview/a.out
0804a000-0804b000 rw-p 00001000 fd:01 14844      /home/Alex/DaNei/Interview/a.out
4427b000-4429a000 r-xp 00000000 fd:01 393917     /usr/lib/ld-2.17.so
4429a000-4429b000 r--p 0001e000 fd:01 393917     /usr/lib/ld-2.17.so
4429b000-4429c000 rw-p 0001f000 fd:01 393917     /usr/lib/ld-2.17.so
442a3000-4445b000 r-xp 00000000 fd:01 393918     /usr/lib/libc-2.17.so
4445b000-4445c000 ---p 001b8000 fd:01 393918     /usr/lib/libc-2.17.so
4445c000-4445e000 r--p 001b8000 fd:01 393918     /usr/lib/libc-2.17.so
4445e000-4445f000 rw-p 001ba000 fd:01 393918     /usr/lib/libc-2.17.so
4445f000-44462000 rw-p 00000000 00:00 0 
b7752000-b7753000 rw-p 00000000 00:00 0 
b7769000-b776b000 rw-p 00000000 00:00 0 
b776b000-b776c000 r-xp 00000000 00:00 0          [vdso]
bfe13000-bfe34000 rw-p 00000000 00:00 0          [stack]

        实际上,第一行是代码区所占的内存空间,804800-804900,实际上几乎所有的linux程序代码段都是从804800开始的,第二行是全局栈区所占的内存空间,第三行是堆空间所占的内存空间,最后一行是局部栈所占的内存空间。

        同时,我们会发现除了代码段的权限是r-xp(可读-不可写-可执行-私有保护)之外,其他三个都是不可执行的。

       



2015-09-24 16:32:05 Deep_l_zh 阅读数 525

摘要:通过编写一个例程addr.c,总结分析了linux应用程序的地址布局,包括代码段,数据段,BSS段,堆(heap)和栈(stack)的分布,分析了程序在运行之后,各个变量常量动态申请分配的内存分别在什么位置。


一、程序的构成及内存分布

    linux应用程序的构成包括:代码段,数据段,BSS段(未初始化数据段),堆和栈。

    上面组成部分当程序在linux中运行的时候,分布情况如下:

    从下到上依次是代码段,数据段,BSS段,堆和栈,其中代码段的起始地址固定为0x08048000,栈按照自上向下生长,堆按照自下向上生长。


二、数据存放的位置

    1.代码段:代码,全局常量const,字符串常量。

    2.数据段:全局变量,静态变量。

    3.堆:动态分配的区域

    4.栈:局部变量,局部只读变量

    总之,凡事被static修饰的变量,全部放在数据段,无论是全局变量还是局部变量。

 

三、分析addr.c查看数据存放位置

    这里我写的addr.c函数如下:

<span style="font-size:18px;">#include<stdio.h>
#include<malloc.h>
 
int g_a;
int g_b=0;
 
static int s_g_a;
static int s_g_b=0;
 
const int c_g_b=0;
 
 
int main(void)
{
    intl_a;
    intl_b=0;
   
    staticint s_l_a;
    staticint s_l_b=0;
   
    constint c_l_b=0;
   
    int*m_l_a;
    m_l_a=malloc(sizeof(int));
   
   
    printf("1-g_aaddress is:%0x,g_a is:%d\n",&g_a,g_a);
    printf("2-g_baddress is:%0x,g_b is:%d\n",&g_b,g_b);
    printf("3-s_g_aaddress is:%0x,s_g_a is:%d\n",&s_g_a,s_g_a);
    printf("4-s_g_baddress is:%0x,s_g_b is:%d\n",&s_g_b,s_g_b);
    printf("5-c_g_baddress is:%0x,c_g_b is:%d\n",&c_g_b,c_g_b);
   
    printf("6-l_aaddress is:%0x,l_a is:%d\n",&l_a,l_a);
    printf("7-l_baddress is:%0x,l_b is:%d\n",&l_b,l_b);
    printf("8-s_l_aaddress is:%0x,s_l_a is:%d\n",&s_l_a,s_l_a);      
    printf("9-s_l_baddress is:%0x,s_l_b is:%d\n",&s_l_b,s_l_b);
    printf("10-c_l_baddress is:%0x,c_l_b is:%d\n",&c_l_b,c_l_b);
   
    printf("11-m_l_aaddress is:%0x,m_l_a is:%d\n",m_l_a,*m_l_a);     
    while(1);
 
    return0;
    }</span>

    主要是查看不同的变量,包括全局未初始化,全局初始化,静态全局未初始化,静态全局初始化,局部未初始化,局部初始化,静态局部未初始化,静态局部初始化和动态分配的内存。

    按照下面的指令操作:

    #gcc addr.c –o addr

    #./addr

    运行起来之后,重新打开一个终端,然后:
    # ps aux |grep ./addr

    这时候就找到addr这个进程运行的pid号,我的如下;

   

    这里是12501,然后接着输入:

    # cat /proc/12501/maps

    这时候我要的信息就出来了:

    

    下面我做了一张表格,把这个maps和我们刚才的变量对应起来:

 

名称

地址范围

包含变量

代码段

08048000-08049000

c_g_b

数据段

08049000-0804a000

g_a,g_b,s_g_a,s_g_b,s_l_a,s_l_b

090d4000-b78fe000

m_l_a

bfc8a000-bfcab000

l_a,l_b,c_l_b

BSS段

0804995c-0804997c

g_a,g_b,s_g_a,s_g_b,s_l_a,s_l_b

    这里和我们刚才总结的是一样的吧,最后的BSS段是因为要借助另一个方法来查看,输入:

    # readelf –S addr

    我的出现如下信息:

    可以看到bss的信息了吧,往上面的表里面填,这里给了一个起始地址,一相对数据段起始地址的偏移量和这段的大小,大小为多少呢,20,结束地址我们也可以计算得到,往里面填进去,可以完整的得到上表。

    这里为什么BSS段和数据段包含的变量都重合了呢?难道数据段里面的东西都包含在了BSS段吗?显然不是的!!!因为我的程序即使初始化了,也给的是0,程序默认是没有进行初始化的,我们的BSS段就是数据段里面未初始化的数据存放的地方,不信你可以把g_b初始化的时候改为1,再按照以上方法,BSS段里面就没有g_b了,我反复做过几次这个实验验证自己的想法,一开始也因为这个卡了一会儿,不得不说自己做一遍,理解真的会加深很多。

    这篇帖子就总结到这里吧,如有不正确的地方还请指出,大家共同进步!

2012-09-19 11:34:45 wellmikelan 阅读数 378

一. 机制

    Linux 程序不允许直接访问物理内存。而且,如果机器配置正确且有足够的交换空间,Linux 允许程序访问比实际物理地址大得多的内存空间。


二. 大内存分配

    应用程序所分配的内存是由 Linux 内核管理的。每次程序请求内存或者尝试读写它已经分配的内存时,便会由 Linux 内核接管并决定如何处理这些请求。

    开始时,内核只是通过使用空闲的物理内存来满足应用程序的内存请求,但是当物理内存耗尽时,它便会开始使用所谓的交换空间(swap space)。在 Linux 系统中,交换空间是一个在安装系统时分配的独立的磁盘区域。内核会在物理内存和交换空间之间移动数据和程序代码,使得每次读写内存时,数据看起来总像是已存在于物理内存中,而不管在你访问它们之前,它们究竟是在哪里。getconf PAGESIZE

    最终,当应用程序耗尽所有的物理内存和交换空间,或者当最大栈长度被超过时,内核将拒绝此后的内存请求,并可能提前终止程序的运行。

    那么,这种明显的没有限制的内存供应和在内存耗尽前系统提前终止进程的做法是否意味着,对 malloc() 函数的返回值进行检查没有意义呢?显然不是。在 C 语言中,一个最常见的问题是试图在一个已分配的内存块之后写数据。在这种情况下,程序可能不会立即终止,但是可能已经覆盖了 malloc() 库例程内部使用的一些数据,导致后续的 malloc() 调用失败。此时不是因为内存不够,而是内存的结构已经被破坏。


三. 空指针

    对于空指针的写操作,一律不容忍。但是对于空指针的读操作,情况则不同。Linux 在 GNU C 库函数中中容忍了空指针读操作,但如果不使用 GNU C 函数库,则从零地址处读取数据也不允许。


四. realloc()

    该函数用于改变先前已经分配的内存块长度。为了完成这一任务,realloc() 函数可能不得不移动数据,因此,必须使用新指针而不是之前的指针去访问内存。

    另一个需要注意的问题是,如果 realloc() 无法调整内存大小,将返回空指针。因此,如果代码类似下面这样:

    pointer = malloc(BLOCK_SIZE);

    ......

    pointer = realloc(pointer, BLOCK_SIZE * 10);

    如果 realloc() 调用失败,它将返回空指针,pointer 将为 NULL,而先前用 malloc() 分配的内存将无法再通过 pointer 进行访问。因此,在释放老内存块之前,最好的办法是先用 malloc() 请求一块新内存,再通过 memcpy() 调用把数据从老内存块复制到新的内存块。


五. alloca

    程序员这么多年,到今天才听说有个 alloca,在 alloca.h 中声明。该函数用于在调用者的堆栈上分配内存,当调用者返回时自动释放。一听到这个,就能明白为什么不推荐使用,用堆栈的内存根本不用这么费劲。另外,移植性也是一个问题,在没有堆栈的机器上无法实现,因为除了递归调用,堆栈并非必需,因为在编译时可以知道局部变量、参数和返回地址所需空间的固定大小,并可以将其分配于 BSS 段。

2012-03-19 19:03:10 cwj649956781 阅读数 686
 

1.malloc函数可以保证其返回的内存是地址对齐的,所以它可以被转换为任何类型的指针。
2.程序能够分配大大超出机器物理内存容量的内存。因为存在“虚拟内存”。
3.应用程序所分配的内存是由linux内核管理的,内核只是通过使用空闲的物理内存来满足应用程序的内存请求,但

是当物理内存消耗尽时,它便会开始使用所谓的交换空间。 在linux系统中,交换空间是一个在安装系统时分配的独

立的磁盘区域。
4.应用程序所看到的内存是:按需换页的虚拟内存系统. ITer看到的所有内存全是虚拟的。当所访问的内存在物理上

并不存在时,就会产生一个页面错误并将控制权交给内核。
5.当应用程序耗尽所有物理内存和交换空间时,或者当最大栈长度被超过时,内核将拒绝此后的内存请求,并可能提

取终止程序的运行。
6.在有必要的情况下,内核会把该页内存放入到交换空间中,以腾出实际物理内存供程序内存申请使用,需要使用时

,再重交换空间提取出来,然后把一块内存放入交换空间。(空间不够时,需要腾出一个页到交换空间,然后让交换

空间的页进入内存中去。)
7.程序常出的内存访问错:访问,写一个内存块之后的数据。 即超出了该内存块的写操作。
8.只有操作系统才知道物理内存是如何安排的,它不仅为用户程序管理内存,同时也为用户程序提供彼此之间的隔离

保护。
9。释放内存:使用free(),他会把内存释放给malloc内存管理器。这样分散的内存块又有可能重新合并在一起了哦。
10.调用free()使用的指针必须是指向malloc calloc realloc 调用所分配的内存。
11.free()之后的内存是不可以读写的哦。由malloc函数库来接手管理了哦。
12.calloc它所分配的内存将全部初始化为0. realloc 函数用来改变先前已经分配好的内存块的长度,增加或则减少

长度。


二。文件锁定
原子操作:即锁定时,系统将不允许任何其他的事情发生。
使用锁文件,也可以达到进程间的同步协调啊。所有的同步,东东,不虚使用具有全局性的变量或者文件,否则无法

通过这些东东来判断是否被占用,因为他们是不同的进程或者函数,需要协调的话,当然是全局了哦。
比如:锁文件。 他是保存在磁盘上的,当然具有全局性, 比如:线程信号量,互斥量,他们是由进程来管理的,而

不是线程函数。所以具有全局。 比如:IPC进程间通信机制:他们由操作系统来统一管理,所以也可以通过他们来同

步撒。 我去。

程序的内存分配

阅读数 184

没有更多推荐了,返回首页