每个程序被运行起来后,它将拥有自己独立的虚拟地址空间,这个虚拟地址空间的大小由计算机的硬件平台决定。
一般来说,C语言指针大小的位数与虚拟空间的位数相同。
Linux OS 对进程虚拟地址空间的分配:
Operating System 1GB
0xC0000000 ----------------------------- ----------
User Process 3GB
0x00000000 ----------------------------- ----------
windows默认的是2GB+2GB
---------------------------------------------------------------------------------------------------------------------------
PAE(Physical Address Extension)
缺省
-----------------------------------------
装载的方式
1.覆盖装入
2.页映射
-----------------------------------------
进程的建立
1.创建一个独立的虚拟地址空间
2.读取可执行文件头,并且建立虚拟空间与可执行文件的映射关系(装载过程)
由于可执行文件在装载时实际上是被映射的虚拟空间,所以可执行文件很多时候又被叫做映像文件(Image)。
Linux 中将进程虚拟空间中的一个段叫做虚拟内存区域(VMA);在windows中将其叫做虚拟段(virtual section)。
映射关系保存在OS内部的一个数据结构。
3.将CPU的指令寄存器设置成可执行文件的入口地址,启动运行。
涉及内核堆栈和用户堆栈的切换、CPU运行权限的切换。
从进程的角度可以看做是OS执行了一条跳转指令,直接跳转到可执行文件的入口地址。(如ELF文件头保存有入口地址)
-------------------------------------------------------------------------------------------------------------------------------------
进程虚存空间分布
1.ELF文件链接视图和执行视图
ELF可执行文件引入了一个概念叫做“Segment”,一个“Segment”包含一个或多个属性类似的“Section”。以“Segment”为单位映射,可以减少页内部碎片。
在将目标文件连接成可执行文件时,链接器会尽量把相同权限属性的段分配在同一“Segment”。
描述“Section”(链接视图)的结构叫做段表;描述“Segment”(执行视图)的结构叫做程序头,描述了ELF文件该如何被OS映射到进程的虚拟空间。
查看Section
查看segment
Program Header Table用来保存Segment的信息
----------------------------------------------------------------------------------------------------------------------------------------------
堆和栈
VMA除了用来映射可执行文件中的各个“Segment”以外,还用来对进程的地址空间进行管理。很多情况下,一个进程中的堆和栈分别都有一个对应的VMA。
查看进程的虚拟空间分布
第一列 VMA地址范围
第二列 VMA权限,p表示私有,s表示共享
第三列 偏移,表示VMA对应的Segment在映像文件中的偏移
第四列 映像文件所在设备的主设备号和次设备号
第五列 映像文件的节点号
第六列 映像文件的路径
主设备号和次设备号及文件节点号都是0,表示它们没有映射到文件中,这种VMA叫做匿名虚拟内存区域。
“vdso”是一个内核的模块,进程可以通过访问它来跟内核进行通信。
------------------------------------------------------------------------------------------------------------------------------
进程栈初始化
进程启动时,须知道一些进程运行的环境,最基本的就是系统环境变量和进程的运行参数。
常见做法是OS在进程启动前将这些信息保存到进程的虚拟空间的栈中。
进程在启动后,程序的库部分会把堆栈里的初始化信息中的参数信息传递给main()函数。
-------------------------------------------------------------------------------------------------------------------------------
Linux内核装载ELF过程简介
在bash 下输入命令执行某个ELF程序:
bash进程调用fork()系统调用创建一个新的进程,新的进程调用execve()执行指定的ELF。原先的 bash进程继续返回等待刚才启动的新进程结束。
在进入execve()系统调用之后,Linux内核开始进行真正的装载工作:
1.查找被执行的文件,如果找到文件,读取文件的前128个字节以判断文件的格式。
2.搜索和匹配合适的可执行文件装载处理过程。
1>根据ELF的程序头表的描述,对ELF进行映射。
2>初始化ELF进程环境。
3>将系统调用的返回地址修改为ELF可执行文件的入口点(静态链接为文件头e_entry所指地址;动态链接为动态连接器)。
3.系统调用返回,新的程序开始执行。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------、
Windows PE 的装载
RVA (Relative Virtual Address),表示一个相对虚拟地址,它是相对于PE文件的装载基地址的一个偏移地址。
每个PE文件在装载时都会有一个装载目标地址,即基地址。