进程虚拟地址空间之数据分区存放【转】

转自:http://blog.csdn.net/bullbat/article/details/7318269 

作者:bullbat   

 

       在前面的《对一个程序在内存中的分析 》中很好的描述了程序在内存中的布局,这里对这个结果做些总结和实验验证。下面以Linux为例(实验结果显示windows上的结果也一样)。

       我们还是利用前面看到过的这个图,如下图:32位X86机器的内存布局图,内存主要分为栈、堆、BSS段、数据段、代码段5个段。

 

       代码段:代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。程序在被载入内存后,会被分为很多小的区(section),有一个rodata区(也就是常量区),常量区的数据内存中的位置为代码段,存放的数据为字符串、const修饰的常量(全局的或是静态的,局部的存放在栈中)、如Char* s=”Hello,World”,那么指针s所指向的字符串”Hello,World“存放在rodata区,而这个字符串的地址也就是指针s存放在数据段中(程序载入内存中为.data区)。再如,static char *const s=”hello,world";那么这时候不仅"hello,world"字符串存放在rodata区,指针s也同样。

 

       数据段:数据段(data segment)通常是指用来存放程序中已初始化的全局变量或者静态变量的一块内存区域。数据段属于静态内存分配。原则上数据段所对应的内存区数据是可以改变的。这里没有提到局部变量,这是因为局部变量一般都存放在栈中。局部变量不管是否有const修饰都存放在栈中,例如char *const lcp="999";字符窜"999"存放在代码段的rodata区,这个没有说的,而它对应的地址lcp指针存放在栈中;

 

      BSS段:BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。例如全局变量int i;静态变量static int si;都存放在这里面。

 

      堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减),要注意的是,当分配的数据大小操作内核的限制时,内核采用匿名映射的方式实现而不是从堆中分配内存。

 

       栈(stack):栈又称堆栈, 是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段或代码段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。

在进程被载入内存中时,基本上被分裂成许多小的节(section)。我们比较关注的是几个主要的节:

(1) .text 节
    .text 节基本上相当于二进制可执行文件的.text部分,它包含了完成程序任务的机器指令。
该节标记为只读,如果发生写操作,会造成segmentation fault。在进程最初被加载到内存中开始,该节的大小就被固定。

(2).data 节
    .data节用来存储初始化过的变量,如:全局int a =0 ; 该节的大小在运行时固定的。

(3).bss 节
     栈下节(below stack section ,即.bss)用来存储为初始化的变量,如:int a; 该节的大小在运行时固定的。

(4) 堆节
      堆节(heap section)用来存储动态分配的变量,位置从内存的低地址向高地址增长。内存的分配和释放通过malloc() 和 free() 函数控制。

(5) 栈节
      栈节(stack section)用来跟踪函数调用(可能是递归的),在大多数系统上从内存的高地址向低地址增长。
同时,栈这种增长方式,导致了缓冲区溢出的可能性。

 (6).rodata节

      常量区,全局或静态const变量、指针存放区。

(7)环境/参数节
       环境/参数节(environment/arguments section)用来存储系统环境变量的一份复制文件,
进程在运行时可能需要。例如,运行中的进程,可以通过环境变量来访问路径、shell 名称、主机名等信息。
该节是可写的,因此在格式串(format string)和缓冲区溢出(buffer overflow)攻击中都可以使用该节。
另外,命令行参数也保持在该区域中。

 

我们以Linux为例,看下面的代码:

[cpp] view plain copy

 

 print?

  1. <span style="font-size:18px;">#include <stdio.h>  
  2. #include <stdlib.h>  
  3. /*全局已初始化数组,存放在数据段中*/  
  4. char a[100]="22222222";  
  5. /*全局未初始化数组,存放在BSS段*/  
  6. int b[100];  
  7. /*全局未初始化数据,存放在BSS段*/  
  8. int c;  
  9. /*全局已初始化字符串指针,字符串存放在代码段的.rodata区,他的地址p存放在数据段中*/  
  10. char * p="11111111";  
  11. /*全局已初始化整形常量,存放在代码段的.rodata区*/  
  12. const int ci=9;  
  13. /*全局已初始化指针常量,字符创和他的地址cp都存放在代码段的.rodata区*/  
  14. char *const cp="88888";  
  15.   
  16. int main()  
  17. {  
  18.     /*局部未初始化数据,存放在栈中*/  
  19.     int li;  
  20.     /*局部已初始化字符串指针,字符串内容存放在代码段的.rodata区,他的地址lp存放在栈*/  
  21.     char *lp="66666";  
  22.     /*局部已初始化数据,存放在栈中*/  
  23.     char la[100]="4444444";  
  24.     /*静态未初始化数据,存放在BSS段*/  
  25.     static int si;  
  26.     /*静态已初始化数据,存放在数据段在数据段中*/  
  27.     static int sii=5;  
  28.     /*局部常量存放在在栈中*/  
  29.     const int lci=2;  
  30.     /*局部已初始化字符串指针常量,字符串存放在代码段的.rodata区,他的地址lcp存放在栈中*/  
  31.     char *const lcp="89999";  
  32.     /*静态已初始化字符串指针常量,字符串和他的地址scp都存放在代码段的.rodata区*/  
  33.     static char *const scp="kkkkk";  
  34.     /*从堆中申请内存,mc存放在栈中*/  
  35.     char *mc=(char*)malloc(100);  
  36.     /*函数的参数以栈的形式传入,前面的字符串从代码段的.rodata区获得*/  
  37.     printf("a[0]:%c\np[0]:%c\nla[0]:%c\nlp[0]:%c\n",a[0],p[0],la[0],lp[0]);  
  38.     /*参数为字符串常量,从代码段的.rodata区取出*/  
  39.     printf("hello world\n");  
  40.     return 1;  
  41. }</span>  

为验证我们在代码中的注释,我们对上面代码在fedora12上编译:

gcc -S segment.c 

上面编译命令生成segment.c对应的汇编代码:

.file "segment.c"

;a为全局.data区

.globl a

.data

.align 32

.type a, @object

.size a, 100

a:

.string "22222222"

.zero 91

;b和c为.comm也就是BSS区

.comm b,400,32

.comm c,4,4

.globl p

.section .rodata

;全局p内容,即字符串为.rodata区

.LC0:

.string "11111111"

;全局p指针,即字符串的地址为.data区

.data

.align 4

.type p, @object

.size p, 4

p:

.long .LC0

.globl ci

.section .rodata

.align 4

;全局变量ci位于.rodata区

.type ci, @object

.size ci, 4

ci:

.long 9

;全局cp内容和地址都位于.rodata区

.globl cp

.LC1:

.string "88888"

.align 4

.type cp, @object

.size cp, 4

cp:

.long .LC1

;下面为代码中的其余字符串常量,位于.rodata区

.LC2:

.string "66666"

.LC3:

.string "89999"

.align 4

.LC4:

.string "a[0]:%c\np[0]:%c\nla[0]:%c\nlp[0]:%c\n"

.LC5:

.string "hello world"

;.text区,主要存放代码

.text

.globl main

.type main, @function

main:

pushl %ebp

movl %esp, %ebp

andl $-16, %esp

pushl %edi

pushl %esi

pushl %ebx

subl $164, %esp

movl $.LC2, 144(%esp)

movl $875836468, 40(%esp)

movl $3421236, 44(%esp)

leal 48(%esp), %ebx

movl $0, %eax

movl $23, %edx

movl %ebx, %edi

movl %edx, %ecx

rep stosl

movl $2, 148(%esp)

movl $.LC3, 152(%esp)

movl $100, (%esp)

call malloc

movl %eax, 156(%esp)

movl 144(%esp), %eax

movzbl (%eax), %eax

movsbl %al, %esi

movzbl 40(%esp), %eax

movsbl %al, %ebx

movl p, %eax

movzbl (%eax), %eax

movsbl %al, %ecx

movzbl a, %eax

movsbl %al, %edx

movl $.LC4, %eax

movl %esi, 16(%esp)

movl %ebx, 12(%esp)

movl %ecx, 8(%esp)

movl %edx, 4(%esp)

movl %eax, (%esp)

call printf

movl $.LC5, (%esp)

call puts

movl $1, %eax

addl $164, %esp

popl %ebx

popl %esi

popl %edi

movl %ebp, %esp

popl %ebp

ret

.size main, .-main

.section .rodata

.LC6:

;scp和他的内容都放在.rodata

.string "kkkkk"

.align 4

.type scp.2177, @object

.size scp.2177, 4

scp.2177:

.long .LC6

.data

;sii存放在.data

.align 4

.type sii.2174, @object

.size sii.2174, 4

sii.2174:

.long 5

;局部si放在.comm

.local si.2173

.comm si.2173,4,4

.ident "GCC: (GNU) 4.4.2 20091027 (Red Hat 4.4.2-7)"

.section .note.GNU-stack,"",@progbits

 

       对应上面的C代码,不难看出,和我们预想的完全一样,其中关键的地方我用不用的颜色标出来了,从汇编代码中我们同样可以看出,存放于栈中的局部数据其变量并没有出现在汇编代码中,是的,因为他直接存在了栈中,在程序中直接从栈中获得就没有必要用变量来访问了。

 

       我们把这段代码最终还是编译成ELF可执行文件segment.out,然后我们用下面的命令看一下该可自行文件装入内存后的区域划分:

objdump -D segment.out > obj.txt

 

      生成的内容放在了obj.txt文件中,该文件很长,不过我们只看这里我们关心的几个地方,如下:

segment.out:     file format elf32-i386

Disassembly of section .interp:

/*我们调用的函数放在这里*/

Disassembly of section .plt:

08048304 <__gmon_start__@plt-0x10>:

 8048304: ff 35 80 97 04 08     pushl  0x8049780

 804830a: ff 25 84 97 04 08     jmp    *0x8049784

 8048310: 00 00                 add    %al,(%eax)

...

08048314 <__gmon_start__@plt>:

 8048314: ff 25 88 97 04 08     jmp    *0x8049788

 804831a: 68 00 00 00 00        push   $0x0

 804831f: e9 e0 ff ff ff        jmp    8048304 <_init+0x30>

08048324 <__libc_start_main@plt>:

 8048324: ff 25 8c 97 04 08     jmp    *0x804978c

 804832a: 68 08 00 00 00        push   $0x8

 804832f: e9 d0 ff ff ff        jmp    8048304 <_init+0x30>

08048334 <printf@plt>:

 8048334: ff 25 90 97 04 08     jmp    *0x8049790

 804833a: 68 10 00 00 00        push   $0x10

 804833f: e9 c0 ff ff ff        jmp    8048304 <_init+0x30>

08048344 <malloc@plt>:

 8048344: ff 25 94 97 04 08     jmp    *0x8049794

 804834a: 68 18 00 00 00        push   $0x18

 804834f: e9 b0 ff ff ff        jmp    8048304 <_init+0x30>

08048354 <puts@plt>:

 8048354: ff 25 98 97 04 08     jmp    *0x8049798

 804835a: 68 20 00 00 00        push   $0x20

 804835f: e9 a0 ff ff ff        jmp    8048304 <_init+0x30>

/*.text段,这里放的是具体的代码*/

Disassembly of section .text:

08048370 <_start>:

...

08048424 <main>:

...

Disassembly of section .fini:

0804858c <_fini>:

...

/*.rodata区,存放只读数据*/

Disassembly of section .rodata:

080485a8 <_fp_hw>:

 80485a8: 03 00                 add    (%eax),%eax

...

080485ac <_IO_stdin_used>:

 80485ac: 01 00                 add    %eax,(%eax)

 80485ae: 02 00                 add    (%eax),%al

080485b0 <__dso_handle>:

 80485b0: 00 00                 add    %al,(%eax)

 80485b2: 00 00                 add    %al,(%eax)

 80485b4: 31 31                 xor    %esi,(%ecx)

 80485b6: 31 31                 xor    %esi,(%ecx)

 80485b8: 31 31                 xor    %esi,(%ecx)

 80485ba: 31 31                 xor    %esi,(%ecx)

 80485bc: 00 00                 add    %al,(%eax)

...

080485c0 <ci>:

 80485c0: 09 00                 or     %eax,(%eax)

 80485c2: 00 00                 add    %al,(%eax)

 80485c4: 38 38                 cmp    %bh,(%eax)

 80485c6: 38 38                 cmp    %bh,(%eax)

 80485c8: 38 00                 cmp    %al,(%eax)

...

080485cc <cp>:

 80485cc: c4 85 04 08 36 36     les    0x36360804(%ebp),%eax

 80485d2: 36 36 36 00 38        add    %bh,%ss:(%eax)

 80485d7: 39 39                 cmp    %edi,(%ecx)

 80485d9: 39 39                 cmp    %edi,(%ecx)

 80485db: 00 61 5b              add    %ah,0x5b(%ecx)

 80485de: 30 5d 3a              xor    %bl,0x3a(%ebp)

 80485e1: 25 63 0a 70 5b        and    $0x5b700a63,%eax

 80485e6: 30 5d 3a              xor    %bl,0x3a(%ebp)

 80485e9: 25 63 0a 6c 61        and    $0x616c0a63,%eax

 80485ee: 5b                    pop    %ebx

 80485ef: 30 5d 3a              xor    %bl,0x3a(%ebp)

 80485f2: 25 63 0a 6c 70        and    $0x706c0a63,%eax

 80485f7: 5b                    pop    %ebx

 80485f8: 30 5d 3a              xor    %bl,0x3a(%ebp)

 80485fb: 25 63 0a 00 68        and    $0x68000a63,%eax

 8048600: 65                    gs

 8048601: 6c                    insb   (%dx),%es:(%edi)

 8048602: 6c                    insb   (%dx),%es:(%edi)

 8048603: 6f                    outsl  %ds:(%esi),(%dx)

 8048604: 20 77 6f              and    %dh,0x6f(%edi)

 8048607: 72 6c                 jb     8048675 <scp.2177+0x61>

 8048609: 64 00 6b 6b           add    %ch,%fs:0x6b(%ebx)

 804860d: 6b 6b 6b 00           imul   $0x0,0x6b(%ebx),%ebp

 8048611: 00 00                 add    %al,(%eax)

...

08048614 <scp.2177>:

 8048614: 0b                    .byte 0xb

 8048615: 86 04 08              xchg   %al,(%eax,%ecx,1)

Disassembly of section .eh_frame_hdr:

...

/*.data区,存放已初始化的全局和静态数据*/

Disassembly of section .data:

080497a0 <__data_start>:

...

080497c0 <a>:

 80497c0: 32 32                 xor    (%edx),%dh

 80497c2: 32 32                 xor    (%edx),%dh

 80497c4: 32 32                 xor    (%edx),%dh

 80497c6: 32 32                 xor    (%edx),%dh

...

08049824 <p>:

 8049824: b4 85                 mov    $0x85,%ah

 8049826: 04 08                 add    $0x8,%al

08049828 <sii.2174>:

 8049828: 05                    .byte 0x5

 8049829: 00 00                 add    %al,(%eax)

...

/*.bss区,存放未初始化的全局和静态变量*/

Disassembly of section .bss:

08049840 <completed.5934>:

 8049840: 00 00                 add    %al,(%eax)

...

08049844 <dtor_idx.5936>:

 8049844: 00 00                 add    %al,(%eax)

...

08049848 <si.2173>:

...

08049860 <b>:

...

080499f0 <c>:

 80499f0: 00 00                 add    %al,(%eax)

...

Disassembly of section .comment:

...

      从不同颜色标出数据可以很清楚的看出,上面的的数据和前面我们分析的汇编代码完全一致。Windwos的情况类似,可以在工程->设置->C/C++分类中设置为Listing Files然后在列表中选择Assembly with Source Code,这样在debug或release文件夹下会生成C对应的汇编代码。当然也可以在调试的过程中直接看其每条C语句对应的汇编代码。

时间: 2024-10-10 07:03:56

进程虚拟地址空间之数据分区存放【转】的相关文章

ECS数据分区丢失问题处理方法、常见误区和最佳实践

本期分享嘉宾 子岳 多年客户系统和网络运维经验,擅长系统故障分析和排查,目前聚焦VPC网络相关问题处理. ECS数据分区丢失问题处理方法.常见误区和最佳实践 概述 我们在处理客户磁盘相关问题时,经常遇到操作系统中数据盘分区丢失的情况.本文档介绍了Linux和Windows下常见的数据分区丢失问题,以及对应的处理方法,同时给出客户最佳实践以避免可能的数据丢失风险.重要 在对数据修复之前,首先需要对分区丢失的数据盘创建快照.快照创建完成后再进行尝试修复,如果在修复过程中出现问题,可以通过快照回滚还原

Excel表格数据分区密码怎么设置

  大家在使用office2010制作Excel表格的时候不知道office2010怎么设置Excel表格数据分区密码,其实方法很简单哦,只要在office2010Excel表格里新建区域然后选中分区就可以进行密码设置了哦,下面就和小编一起来看看吧. office2010设置Excel表格数据分区密码方法: 第一步:首先打开office2010 Excel表格,输入数据.   第二步:点击主菜单上的审阅,点击允许用户编辑区域.   第三步:点击新建区域,选中所选区域,并设置密码123(密码自己随

购物车中数据的存放方式

aierong [原作] 第一次做BToC站点的购物车,总结了一下购物车中数据的存放方式 方式1:用会话 会话中可以存放任何类型数据,每个用户有一个唯一的会话ID,用此ID区分不同用户的购物车会话数据是存放在WEB服务器的内存中的,如果使用购物车的用户比较多的话,这样一来将占用大量服务器资源会话有一小缺陷,它是依靠COOKIE来与用户通讯的,一旦用户关闭COOKIE,使用会话将比较麻烦 当然在ASP.NET中还有另2种方式保存会话数据一种是指定另一台服务器来存放会话数据,这样一来可以有效分担WE

在Linux上利用数据分区功能提高可伸缩性和性能

本文首先考察了 DB2 UDB for Linux.UNIX 和 Windows 中的 IBM DB2 Universal Database 数据分区功能(Data Partitioning Feature,DPF)在性能和可伸缩性方面的优点.然后,完成在 SUSE Linux Enterprise Server 上安装和配置具有 DPF 功能的 DB2 的步骤.您还将学习到一些重要的概念和设计方面的考虑,这些将帮助您快速掌握 SUSE Linux Enterprise 环境中 DPF 的安装.

jsp-在JSP中能不能将一个表中的数据全部存放到作用域里然后在页面上有选择性的显示

问题描述 在JSP中能不能将一个表中的数据全部存放到作用域里然后在页面上有选择性的显示 在页面中可不可以通过判断让他满足某个条件是显示相应的数据 解决方案 你说的有选择的显示是根据条件查询呢,还是根据页面选择需要展示的数据列,然后再展示数据(类似数据库客户端的隐藏/显示某些列的数据呢? 前者就是条件查询,后者实现时可以让页面选择展示列名称,后台sql查询相应的列再进行数据展示. 解决方案二: 使用JSTL的c:if中的一个标签 例子<c:if test="${1<2}"&g

Apache Geode/GemFire 数据分区和路由机制浅析

本篇文章主要讲解Apache Geode/GemFire 是如何进行数据分区的. GemFire和大多数分布式系统一样都采用 Hash 的方式对数据进行分区,将 Entry 数据分布到 PartitionedRegion 当中,大家都知道 Entry 数据主要保存在 ConcurrentHashMap 中,ConcurrentHashMap存放在 Bucket 中,在 PR 服务器启动后会为 PartitionedRegion创建相应的Bucket 来保存这个ConcurrentHashMap.

linux 管道 父进程写入管道的数据,其他进程没有处理怎么办?

问题描述 linux 管道 父进程写入管道的数据,其他进程没有处理怎么办? #include #include int main(void) { int n; char line[MAXLINE]; int fd[2]; pid_t pid; //create the pipe if(pipe(fd)<0) err_sys("pipe error!"); if((pid = fork()) < 0 ){ err_sys("fork error"); }

mysql-【求救】关于MYSQL CLUSTER数据文件存放节点错误问题

问题描述 [求救]关于MYSQL CLUSTER数据文件存放节点错误问题 SQL节点,DATA节点均已配置正常,但是出现一个匪夷所思的问题. 在33节点执行了建库脚本及建表脚本,发现数据文件在33和34两个SQL节点存在,按理说数据文件应该数据节点上的DATADIR. Connected to Management Server at: localhost:1186 Cluster Configuration [ndbd(NDB)] 2 node(s) id=2 @192.168.1.22 (m

服务器端JSON数据或者xml数据如何存放和设置

问题描述 服务器端JSON数据或者xml数据如何存放和设置 本人初学,正在做关于手机app版本升级这块,手机app要从网上得到app version信息,这就需要用到JSON或者xml.现在问题是我知道JSON和xml数据如何解析,但不知道如何从服务器或者其他网页上等得到JSON和xml数据,还有我怎么才能生成JSON或xml数据文件,要以什么样的形式(嵌到代码里还是直接把生成的文件copy到服务器上等等就可以)放到网页上或者服务器上,生成JSON数据是不是还要用到servlet,这个没研究过.