程序员的自我修养—精华(elf文件虚拟内存)
Posted WChango
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了程序员的自我修养—精华(elf文件虚拟内存)相关的知识,希望对你有一定的参考价值。
程序
我们写的程序,需要经过(编译,链接)得到 最终的exe/elf(产生到 磁盘上)。
如果我们要运行他,必须要把他加载到 内存里! 因为cpu访问内存的速度 要比访问磁盘,速度快千万倍
cpu的位数,是指: 他一次性能够进行运算的 整数的宽度,因为cpu是在ALU里运算数据
所以, cpu的位数 其实是 ALU的宽度(即,数据总线的 条数)
但是, cpu的位数 不一定等于 地址总线的 条数,他等于的是 数据总线的条数
比如虚拟地址是4GB,(物理内存不一定是4GB!!),他的布局格式是:
-
[0 - 3GB]
这个地址区域,称为 用户空间-
[0 - 128MB]
是forbidden禁止的! 他是禁止访问的,连访问都不能,更不能写!! -
[128MB - 3GB]
,这是我们程序可以使用的 虚拟地址区域
第一个区域,他划分为有:[.text] [.data] [.bss]
我们知道,一个程序无非是:(指令) + (数据)
指令是在:.text段, 数据是在:.data段 和 .bss段紧接着 第二个区域: heap 堆空间
第三个区域:用来放到dll共享库的!
比如,你用了很多scanf
这种函数,他在头文件里 只有声明,并没有定义。 但是,等到链接时,程序会自动的 链接一些系统的库。第四个区域:
[.stack]
段
因为,我们程序的运行 就是从入口函数开始递归,这个段就是供我们函数运行的;比如,你的局部变量 就会在这个.stack
段里。第五个区域: 放命令行参数、环境变量。
main函数的参数 argv这些
-
-
[3 - 4GB]
这个地址区域,称为 内核空间,即OS运行的空间
第一个区域是:ZONE_DMA
DMA: direct memory access
,加速 磁盘和内存 交换数据用的
在没有DMA技术前,磁盘和内存交换数据时: 磁盘数据 必须经过总线,流经 cpu的寄存器,最终才能到达内存
这是对cpu的 极大浪费。
有了DMA,比如加载一个文件 从磁盘 通过总线 到内存中时,不需要使用cpu的寄存器!!!
cpu遇到这种情况,他就会空闲下来,来调度其他的进程!!第二区域: `ZONE_NORMAL` 第三区域: `ZONE_HIGHMEM`
int a = 1;
int b = 0;
int c;
static int d = 1;
static int e = 0;
static int f;
int main(){
int g = 1;
int h = 0;
int i;
static int j = 1;
static int k = 0;
static int l;
return 0;
}
这个程序中,属于 指令的是: int main(){ int g = 1; int h = 0; int i; return 0; }
,即他们是在 .text段。
因为这些,要么是函数,要么是局部变量,可以多次调用 是属于指令。 即注意,局部变量 属于的是 指令!
而对于: 全局变量 + 局部static,这些都是 数据(即这些,在程序的运行,自始至终 都是占着内存的)
其中,已经初始化的 且不为0 的变量 a,d,j
是在 .data
段,即.data段
的大小 是12。
常量(比如"hello"
),他是在 .rodata段
没有初始的 或 初始为0的,是在.bss
段。
关于.bss段
,有几个信息:
-
在.obj文件里,
[elf header] [.text] [.data] [.bss] [.comment] [section table]
但其实你会发现,.comment段 和 .bss段
在文件里的地址,其实是一样的!!
而这两个段 大小都不是0,为什么是一样的呢?
其实,.bss
段 他在obj文件里 其实是不占空间的!!! 其实就没有这个段,.data
后面就是.comment
.bss
段 只会占的是: 虚拟空间里的内存!! 而不占.obj文件里的空间而,这个
.bss
段 在.obj文件 是不存在的,那他怎么知道 这个段里的那些全局/static变量呢?
通过读section table
段表,他里面会记录 每个段的详细信息,bss的信息 可以通过他来获取.bss段,没必要在文件中存储。这是因为: .data段里存的 都是初始值,即程序一运行 这些值就必须初始化为(用户指定的值)
但是,.bss段里的 他的值 都是0,就不用在文件里存储他了,这样减少文件的大小 -
按理说,该有6个变量 是在.bss段。 但是,为什么
1
不是在.bss段呢?main.c short x = 10; short y = 10; extern void func(); int main(){ func(); } test.c int x; void func(){ x = 20; }
这个项目,在c++里 肯定是错误的。因为有2个x,c++里 所有的符号
变量/函数
,都不能重名。
而在c里,他存在强符号(初始化了的) 和 弱符号(未初始化的)
的区别,这里:short x是强符号,int x是弱符号
(强符号:只能有1个,即初始化的 只能有1个)(弱符号:可以有多个,即未初始的 可以有多个,不管是什么类型)
(在链接后,优先使用强符号; 没有强符号,则优先使用内存最大的弱符号)即,在项目进行链接完后,func()函数里 所使用的x,其实是:short x这个
func函数里他的汇编是: 往x 写4个字节的内存
,这是在单独编译test.c 就确定下来的汇编指令。
而最终链接后,这个汇编指令 肯定是不变的,但这个x 并不是int,而是short
所以,20的4字节是: 14 00 00 00
,前面的14 00 给short x,后面的00 00 给short y
,所以,最终:x = 20, y = 0 {x是指的short这个}
int c;
这个全局变量,他是不在.bss段的。
因为:int b = 0;
初始了,他是强符号虽然c++没有强弱符号,但这个程序 也可能是c的!!
int c;
未初始,弱符号。 他会在另外一个COM
段,不是在bss
段。因为他可能被其他obj里的强符号所顶替
其他的,都是static。static,是本文件可见!!!他是强制性的!!即,即使是一个static的弱符号,在本文件里 也不会被其他强符号替代!
static还有个名字,是local
,即本地的 本文件的。其他文件,也看不到这个文件里的static变量。
os会 运行很多的进程,这些进程 所使用的虚拟空间,其中他们的内核区域[3 - 4GB]
区域 都是共享的,因为只有1个操作系统
但用户空间,他是 独立的,每个进程的 用户空间,肯定都是不同的。
运行、exe文件
当程序要运行时,即变成进程,要进行下面几个步骤:
- 从虚拟地址 到 物理内存 的 映射,创建 页目录 和 页表
- 加载 .text段 和 .data段
- 把该exe的入口地址,写到 pc寄存器里。
ELF header
里,有个entry point address
。到时候这个进程运行时,这个地址 就会放到cpu的 pc寄存器里
,即为该程序的入口地址
exe文件,和 obj文件,非常相似,但多了一个: program headers
exe: [elf header] [program header] [.text] ...
obj: [elf header] [.text] ...
,obj正是因为没有program headers
所以他不能运行
program headers
里,有2个加载页面load
,
- 他指示了OS的loader加载器:要把当前exe程序里的 哪些东西,加载到内存上
- 他也指示了: 要把哪些段 放到 一个页面上
比如,可能第一个LOAD页面 存储了.text段, 第二个LOAD页面 存储了.data + .bss段 - 一个页,是对齐大小是
4kb
(即,一定是4kb的倍数)
这两个LOAD page
,其实就是:磁盘里的 2个页DP: disk page
当要运行时,这两个页 就会映射到 虚拟地址空间 上的页 (虚拟地址空间 和 虚拟内存,是两个概念)
然后再从虚拟地址空间上的页,映射到 物理内存地址上的页, - 当这个进程在running时,他的虚拟地址空间 当然是有很多的页(每个页是4Kb)
有的页,是一些C动态库(比如,libc.so
)
其中有2个页,
一个是: 我们exe的program header
的第一个Load页
、、、、他里面是.text段
[ r x p ] read,executable, private(和其他进程不共享), 不可写(因为是指令)
)
另一个是: 我们exe的program header
的第二个Load页
、、、、它里面是.data .bss段
[r w p] read write private(和其他进程不贡献), 不可执行(因为不是指令)
地址映射
磁盘、虚拟空间、内存,都是以一个PAGE_SIZE = 4kb
来划分的。
磁盘上的页,-> 通过 mmap函数
->,映射到 虚拟地址空间上的页
虚拟地址空间上的页,-> 通过 多级页表映射
->,映射到 物理内存上的页
以上是关于程序员的自我修养—精华(elf文件虚拟内存)的主要内容,如果未能解决你的问题,请参考以下文章
读书笔记|《程序员的自我修养》- 04 可执行文件的装载与进程