常识:
-
磁盘:进行数据的永久化存储。 特点:I/O比较慢
-
内存:支持程序执行所需要的真实空间
物理内存: 内存条。特点I/O快
虚拟内存:
不是内存,是磁盘上提前划分出的一块空间----交换空间。当真实的物理内存不够用时就会将数据置换到虚拟内存,当需要再一次使用时就会将数据再一次置换到真实的物理内存。 -
虚拟地址空间:逻辑上给到每个进程的执行空间(4G)
虚拟地址空间
虚拟地址空间如何产生的?
虚拟地址空间的划分:
32位系统的虚拟地址空间为什么是4G?
一共有32条地址总线
能标志的最小地址:0000 0000
能标志的最大空间:FFFF FFFF
2^32=4G
真实的物理内存为什么不够用:
以32位系统为例:当程序进行执行,每一个进程都会划分一个执行空间大小为4G
a,b,c虽然是局部变量,但不是数据,并不产生符号。拿a来说,在X86体系下生成指令:mov dword ptr[a] ,0Ch ; //将0Ch移动到a的内存里面
因此局部变量的定义最终产生3条mov指令,放在.text段。
疑问:局部变量不应该在栈上吗?为什么会生成指令存放在指令段?
答:
这就是一个概念性的错误。首先函数运行会在当前进程的栈上为函数开辟一块函数栈帧,当局部变量a生成的指令在栈上运行的时候,就会将0Ch放在a这块内存的4字节内存里面。所以当该条指令运行时会在栈上划分出4字节内存来保存OCh.
data1-9都属于数据;最终存储在数据段
- 数据段:
- bss段:存储没有初始化或者初始化为0的数据。例如data 1,2,4,5,7,8
- data段:存储已初始化且初始化值不为0的数据。例如data 3,6,9
Binary中.bss、.data和.rodata段的详细介绍
其余都会生成指令;存储在指令段
编译连接过程中已经有了各个段,虚拟地址空间只是将二进制可执行文件中的对应的段数据读取到虚拟地址空间中。并不是有了虚拟地址空间才划分了这些段,而是二进制可执行文件中已经划分了这些段,虚拟地址空间只是负责将数据读取进来,提供给进程执行所用。
二进制可重定位文件
命令:file + 文件名:可以看文件的类型
relocatable:可重定位文件
命令:readelf -h + 文件名字:可以看文件头部组成
命令:readelf -S +文件名字:打印section headers
命令:objdump -t +文件名子:打印文件符号表
符号:
编译时期生成符号,链接时期还会进行符号的重定位。符号表里面的存放的就是生成的各种符号,什么东西生成符号呢?
所以的数据都会生成符号;
函数名会生成符号
C语言中对符号有强弱之分,C++就没有了
弱符号:没有初始化的非静态数据;
弱符号会在链接的过程中被同名的强符号进行替代
这种情况在linux系统是可以编译通过并执行的,但是在VS2019中由于存在内存保护,即使写越界也是写到被保护的内存而不会将b所在空间填充为0
二进制可执行文件
二进制可执行文件中也没有真实的给.bss段划分空间,也只是让它做上标记
在可执行文件中data1就存在于符号表的.bss段。
当链接完之后,如果说在链接的过程中没有强符号去替换弱符号,就会直接将弱符号转换为强符号
因此链接之后就没有弱符号了,要么弱符号被强符号替换了,要么弱符号直接转换为强符号了。
链接过程进行符号重定位的意思?
1.对于弱符号,单文件编译时会先将其进行COM标记,链接时就会进行强符号替换
2.对于函数,单文件编译时如果没有找到定义就会先进行UND的标记,当进行链接时就会可进行查找替换。
3.对于动态库里面的函数例如:printf() 是在执行的时候才会去动态库进行索引,真正的获取,因此拿命令去找符号表时会显示* UND*
当二进制可执行文件真正执行起来就会产生虚拟地址空间
main函数执行起来的虚拟地址空间
虚拟地址空间只进行逻辑限定,真实存储是在内存
因此当真正使用到该地址时,虚拟地址空间会映射到真实内存,否则只是一个简单的逻辑地址,并没有真实的给到
映射规则:readelf -l +文件名字