Linux下c程序的内存映像

2022-03-21 09:13:56 浏览数 (1)

前言

今天开始分享C语言里面的存储类型、作用域、生命周期、链接属性等知识点,我们写完一个程序,不只说知其,更要知其所以然。

概念简介:

- 存储类 -

(1)存储类就是存储类型,也就是描述C语言变量在何种地方存储。

(2)内存有多种管理方法:栈、堆、数据段、bss段、.text段等,其实这个Linux环境可以查看以ELF结尾的可执行程序,可以看到所说的这些的;一个变量的存储类属性就是描述这个变量存储在何种内存段中。

代码语言:javascript复制
root@ubuntu-virtual-machine:/home/ubuntu# readelf hello-world -S
There are 29 section headers, starting at offset 0x1930:

节头:
[号] 名称              类型             地址              
偏移量
     大小              全体大小          旗标   链接   信息   
对齐
      [ 0]                   NULL             0000000000000000  
  00000000
   0000000000000000  0000000000000000           0     0     
0
     [ 1] .interp           PROGBITS         
  0000000000000238  
   00000238
    000000000000001c  0000000000000000   A       0     0     
   1
    [ 2] .note.ABI-tag     NOTE             
  0000000000000254  00000254
   0000000000000020  0000000000000000   A       0     0     
  4
    [ 3] .note.gnu.build-i NOTE             
    0000000000000274  00000274
   0000000000000024  0000000000000000   A       0     0     
 4
    [ 4] .gnu.hash         GNU_HASH         
   0000000000000298  00000298
   000000000000001c  0000000000000000   A       5     0     
8
    [ 5] .dynsym           DYNSYM           00000000000002b8  000002b8
   00000000000000a8  0000000000000018   A       6     1     8
    [ 6] .dynstr           STRTAB           0000000000000360  00000360
   0000000000000082  0000000000000000   A       0     0     1
   [ 7] .gnu.version      VERSYM           00000000000003e2  000003e2
   000000000000000e  0000000000000002   A       5     0     2
   [ 8] .gnu.version_r    VERNEED          00000000000003f0  000003f0
   0000000000000020  0000000000000000   A       6     1     8
   [ 9] .rela.dyn         RELA             0000000000000410  00000410
   00000000000000c0  0000000000000018   A       5     0     8
  [10] .rela.plt         RELA             00000000000004d0  000004d0
   0000000000000018  0000000000000018  AI       5    22     8
  [11] .init             PROGBITS         00000000000004e8  000004e8
   0000000000000017  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         0000000000000500  00000500
   0000000000000020  0000000000000010  AX       0     0     16
  [13] .plt.got          PROGBITS         0000000000000520  00000520
   0000000000000008  0000000000000008  AX       0     0     8
  [14] .text             PROGBITS         0000000000000530  00000530
   00000000000001a2  0000000000000000  AX       0     0     16
  [15] .fini             PROGBITS         00000000000006d4  000006d4
   0000000000000009  0000000000000000  AX       0     0     4
  [16] .rodata           PROGBITS         00000000000006e0  000006e0
   0000000000000010  0000000000000000   A       0     0     4
  [17] .eh_frame_hdr     PROGBITS         00000000000006f0  000006f0
   000000000000003c  0000000000000000   A       0     0     4
  [18] .eh_frame         PROGBITS         0000000000000730  00000730
   0000000000000108  0000000000000000   A       0     0     8
  [19] .init_array       INIT_ARRAY       0000000000200db8  00000db8
   0000000000000008  0000000000000008  WA       0     0     8
  [20] .fini_array       FINI_ARRAY       0000000000200dc0  00000dc0
   0000000000000008  0000000000000008  WA       0     0     8
  [21] .dynamic          DYNAMIC          0000000000200dc8  00000dc8
   00000000000001f0  0000000000000010  WA       6     0     8
  [22] .got              PROGBITS         0000000000200fb8  00000fb8
   0000000000000048  0000000000000008  WA       0     0     8
  [23] .data             PROGBITS         0000000000201000  00001000
   0000000000000010  0000000000000000  WA       0     0     8
  [24] .bss              NOBITS           0000000000201010  00001010
   0000000000000008  0000000000000000  WA       0     0     1
  [25] .comment          PROGBITS         0000000000000000  00001010
   000000000000002b  0000000000000001  MS       0     0     1
  [26] .symtab           SYMTAB           0000000000000000  00001040
   00000000000005e8  0000000000000018          27    43     8
  [27] .strtab           STRTAB           0000000000000000  00001628
   0000000000000209  0000000000000000           0     0     1
  [28] .shstrtab         STRTAB           0000000000000000  00001831
   00000000000000fe  0000000000000000           0     0     1

说明(这里的话,我简单介绍一些我们平时没有接触到的,大家只需了解即可):

  • dynamic段:用于保存动态链接信息。
  • fini段:用于保存进程退出时的执行程序。当进程结束时,系统会自动执行这部分代码。
  • init段:用于保存进程启动时的执行程序。当进程启动时,系统会自动执行这部分代码。
  • rodata段:用于保存只读数据,如const修饰的全局变量、字符串常量。
  • symtab段:用于保存符号表。

(3)譬如:局部变量分配在栈上,所以它的存储类就是栈;显式初始化为非0的全局变量分配在数据段,显式初始化为0和没有显示初始化(默认为0)的全局变量分配在bss段。

- 作用域 -

(1)作用域是描述这个变量起作用的代码范围。

(2)基本来说,C语言变量的作用域规则是代码块作用域。意思就是这个变量起作用的范围是当前的代码块。代码块就是一对大括号{}括起来的范围,所以一个变量的作用域是:这个变量定义所在的{}范围内从这个变量定义开始往后的部分。(这就解释了为什么变量定义总是在一个函数的最前面)。而且当局部变量和全局变量同名的时候,在main函数里面是先执行局部变量的,也就是说局部变量的作用域是代码块作用域,也就是说一个局部变量可以被访问和使用的范围仅限于定义这个局部变量的代码块中定义式之后的部分:

代码语言:javascript复制
 #include <stdio.h>

 int a=7;
 int main(void)
{
   int a=8;
  a  ;
  printf("the a is %dn",a);


  return 0;

}

演示结果:

代码语言:javascript复制
the a is 9

-生命周期-

(1)声明周期是描述这个变量什么时候诞生(运行时分配内存空间给这个变量)及什么时候死亡(运行时收回这个内存空间,此后再不能访问这个内存地址,或者访问这个内存地址已经和这个变量无关了)的。

(2)变量和内存的关系,就和人(变量)去图书馆借书(内存)一样。变量的生命周期就好象我人借书的这段周期一样。

(3)研究变量的生命周期可以我们理解程序运行的一些现象、理解C语言的一些规则。

- 链接属性 -

(1)大家知道程序从源代码到最终可执行程序,经历的过程:编译、链接。

(2)编译阶段就是把源代码搞成.o目标文件,目标文件里面有很多符号和代码段、数据段、bss段等分段。符号就是编程中的变量名、函数名等。运行时变量名、函数名能够和相应的内存对应起来,靠符号来做链接的。

(3).o的目标文件链接生成最终可执行程序的时候,其实就是把符号和相对应的段给链接起来。

(4)C语言中的符号有三种链接属性:外连接属性、内链接属性、无连接属性(这里只是简单介绍一些有几种链接属性,后面的文章里面会详细的介绍这些链接属性)。

Linux下c程序的内存映像

- 代码段、只读数据段 -

(1)对应着程序中的代码(函数),代码段在linux中又叫文本段(.text)。

(2)只读数据段就是在程序运行期间只能读不能写的数据,const修饰的常量有可能是存在只读数据段的(但是不一定,const常量的实现方法在不同平台是不一样的)。

- 数据段、bss段 -

(1)数据段存:显式初始化为非0的全局变量;显式初始化为非0的static局部变量。

(2)bss段存:显式初始化为0或者未显式初始化的全局变量;显式初始化为0或未显式初始化的static局部变量。这里详细可以看之前写的这篇文章——轻松带你解决c语言堆、栈、数据段、代码段、bss段的疑惑

- 堆 -

(1)C语言中什么样变量存在堆内存中?C语言不会自动向堆中存放东西,堆的操作是程序员自己手工操作的。程序员根据需求自己判断要不要使用堆内存,用的时候自己申请(使用malloc函数),自己使用,完了自己释放(使用free函数释放掉)。

- 文件映射区 -

(1)文件映射区就是进程打开了文件后,将这个文件的内容从硬盘读到进程的文件映射区,以后就直接在内存中操作这个文件,读写完了后在保存时再将内存中的文件写到硬盘中去。

- 栈 -

(1)栈内存区,局部变量分配在栈上;函数调用传参过程也会用到栈。

- 内核映射区 -

(1)内核映射区就是将操作系统内核程序映射到这个区域了。

(2)对于linux中的每一个进程来说,它都以为整个系统中只有它自己和内核而已。它认为内存地址0xC0000000以下都是它自己的活动空间,0xC0000000以上是OS内核的活动空间。

(3)每一个进程都活在自己独立的进程空间中,0-3G的空间每一个进程是不同的(因为用了虚拟地址技术),但是内核是唯一的。

总结

上面的介绍,只是一些概念性的介绍,要详细更加深入的理解Linux内存,可以看这篇文章:https://blog.csdn.net/f22jay/article/details/7925531

0 人点赞