程序结构与进程结构
2015-07-19 18:00
232 查看
学习编程,但是对其根源从未探索,总觉得心里空荡荡的,有时候对根源大的探索是进一步的学习。
在程序写好后,我们进行编译,之后生成一个可执行文件,Linux下为ELF文件,window下会经常看到是一个后缀为.exe的文件,其实为PE文件。那这些文件中结构怎样,定义的变量还有语句是怎么在这个可执行文件中存储的?
我们编译源文件的时候,经过预编译->编译->汇编->链接。最终完成一个可执行文件的生成。
预编译大家可能都熟悉,就是将程序中的注释删掉,宏定义替换,处理条件编译,将include的文件插入到该文件的位置等等。这个是最简单的过程。这一步可利用下面的命令实现,查看预编译后的文件是什么:
那么编译阶段进行什么呢?这个稍微有些复杂,编译器进行词法分析->语法分析->语义分析->中间语言生成->目标代码生成与优化。经过这一步生成汇编代码文件。汇编文件可由下面命令实现:
从上面的图中可以看出,已经初始化过的全局变量与局部静态变量存储在数据区,未初始化的全局变量与局部静态变量存储在.bss区。局部用户定义的变量存储在堆栈区。然后程序语句也就是后面可执行的指令存储在代码区。然后程序在32位机器上执行的时候的映射关系如图后半部分所示。
程序的格式知道了,那么程序运行时候,此时就成为了操作系统一个进程(ps:运行的程序就叫进程),这个过程是怎么进行的呢?下面的图显示其过程:
首先可执行文件将其每个部分映射到虚拟内存空间内,32位可以有4GB空间映射,范围为(0x00000000~0xffffffff),64为可以有17179869184GB的空间可映射,范围为(0x0000000000000000~0xffffffffffffffff)。这里我们以32为系统为例,讲解系统怎么装载程序:
32为系统的可映射的虚拟空间的大小为4GB,其中操作系统内核占去了1GB的空间,剩下的为用户程序可映射的空间,大约有3GB,在windows下操作系统占用2GB的空间,剩下2GB为用户程序所占用的空间,可以通过修改操作系统盘根目录下的Boot.ini,可以将用户空间改到3GB。
装载开始的时候,先创建虚拟地址空间,然后读取可执行文件的头,建立虚拟空间与可执行文件的映射关系。比如上面图中在虚拟内存中建立的.text段,Linux中称为虚拟内存区域(VMA)。最后一步就是将CPU指令寄存器设置成可执行文件的入口。
上面的步骤完成后,只是完成了到虚拟内存空间的映射,注意这里是“虚拟”,也就是说是假的,不是真真的内存,程序的数据还没有真真地进入内存中。这里需要利用CPU的内存管理单元(MMU)进行虚拟内存到物理内存的映射。
这个过程是怎么进行的呢?
从上面图中可以看出,可执行文件的入口地址是0x08048000。CPU开始认为这个就是内存里面的真实的地址,结果发现内存里面此处的数据为空!这个时候就会产生一个页面错误消息。这时候操作系统会调用相应的页错误例程处理这个错误,然后虚拟空间与可执行文件之间的映射就起到非常重要的作用,首先找到空页面所在的虚拟内存区(VMA),计算出相应页面在可执行文件中的偏移,然后在物理内存中分配一个物理页面,建立物理页面与虚拟页的映射关系,然后再执行进程,进程就从错误的位置开始重新执行程序。
有了上面的了解,程序运行时的环境如下:
进程在操作系统的结构大致如下:
在程序写好后,我们进行编译,之后生成一个可执行文件,Linux下为ELF文件,window下会经常看到是一个后缀为.exe的文件,其实为PE文件。那这些文件中结构怎样,定义的变量还有语句是怎么在这个可执行文件中存储的?
我们编译源文件的时候,经过预编译->编译->汇编->链接。最终完成一个可执行文件的生成。
预编译大家可能都熟悉,就是将程序中的注释删掉,宏定义替换,处理条件编译,将include的文件插入到该文件的位置等等。这个是最简单的过程。这一步可利用下面的命令实现,查看预编译后的文件是什么:
gcc -E test.c -o test.i
那么编译阶段进行什么呢?这个稍微有些复杂,编译器进行词法分析->语法分析->语义分析->中间语言生成->目标代码生成与优化。经过这一步生成汇编代码文件。汇编文件可由下面命令实现:
gcc -S test.c -o test.s or gcc -S test.i -o test.s生成汇编文件后,接下来就是要进行汇编,汇编器将生成的汇编语言转换成可执行的机器码,每一条汇编指令对应一条机器指令。利用下面的语句可以完成:
as test.s -o test.o or gcc -c test.s -o test.o最后一步是链接,可能有些疑惑,上面通过汇编已经翻译成机器语言,是可以直接执行的,为什么还要链接呢?这是由于程序在执行的时候会用到一些库文件,这些库包括静态库与动态库文件,链接的过程就是将这些库文件与上面的汇编结果进行综合。具体命令如下:
ld *.o -o test -start-group -lgcc -lgcc_eh -lc-end-group经过链接最终生成可执行文件test,我们通常的编译过程就一句命令解决:
gcc test.c -o test那编译完成的文件,是怎么组成?下图显示了详细的过程:
从上面的图中可以看出,已经初始化过的全局变量与局部静态变量存储在数据区,未初始化的全局变量与局部静态变量存储在.bss区。局部用户定义的变量存储在堆栈区。然后程序语句也就是后面可执行的指令存储在代码区。然后程序在32位机器上执行的时候的映射关系如图后半部分所示。
程序的格式知道了,那么程序运行时候,此时就成为了操作系统一个进程(ps:运行的程序就叫进程),这个过程是怎么进行的呢?下面的图显示其过程:
首先可执行文件将其每个部分映射到虚拟内存空间内,32位可以有4GB空间映射,范围为(0x00000000~0xffffffff),64为可以有17179869184GB的空间可映射,范围为(0x0000000000000000~0xffffffffffffffff)。这里我们以32为系统为例,讲解系统怎么装载程序:
32为系统的可映射的虚拟空间的大小为4GB,其中操作系统内核占去了1GB的空间,剩下的为用户程序可映射的空间,大约有3GB,在windows下操作系统占用2GB的空间,剩下2GB为用户程序所占用的空间,可以通过修改操作系统盘根目录下的Boot.ini,可以将用户空间改到3GB。
装载开始的时候,先创建虚拟地址空间,然后读取可执行文件的头,建立虚拟空间与可执行文件的映射关系。比如上面图中在虚拟内存中建立的.text段,Linux中称为虚拟内存区域(VMA)。最后一步就是将CPU指令寄存器设置成可执行文件的入口。
上面的步骤完成后,只是完成了到虚拟内存空间的映射,注意这里是“虚拟”,也就是说是假的,不是真真的内存,程序的数据还没有真真地进入内存中。这里需要利用CPU的内存管理单元(MMU)进行虚拟内存到物理内存的映射。
这个过程是怎么进行的呢?
从上面图中可以看出,可执行文件的入口地址是0x08048000。CPU开始认为这个就是内存里面的真实的地址,结果发现内存里面此处的数据为空!这个时候就会产生一个页面错误消息。这时候操作系统会调用相应的页错误例程处理这个错误,然后虚拟空间与可执行文件之间的映射就起到非常重要的作用,首先找到空页面所在的虚拟内存区(VMA),计算出相应页面在可执行文件中的偏移,然后在物理内存中分配一个物理页面,建立物理页面与虚拟页的映射关系,然后再执行进程,进程就从错误的位置开始重新执行程序。
有了上面的了解,程序运行时的环境如下:
进程在操作系统的结构大致如下:
相关文章推荐
- Intel INDE(集成原生开发人员体验)的Windows开发指导
- 第二节
- applicationContext.xml 配置文件的存放位置
- 股票学习28(汽车行业补充)
- nginx使用介绍 说明
- 《学习OpenCV》第四章课后题3-b
- AffineTransform入门
- Linux -gdb如何显示宏定义的值
- COJ 2024 仙境传奇(五)——一个天才的觉醒 素数筛
- java事件处理机制(自定义事件)
- 【thinkphp3.2.x】thinkphp3.2.x中有关redis缓存相关的文件
- hosts 的设置将域名与iP在本地绑定
- IOS艺术字及简单的图文混排
- 贪心 Codeforces Round #135 (Div. 2) C. Color Stripe
- [LeetCode] Find Minimum in Rotated Sorted Array II
- Android中打开raw目录下的数据库文件
- 伪装隐藏Nginx,PHP版本号提升服务器安全性
- java微信上传永久素材
- Springmvc 配置json输出的几种方式
- codeforces 557D Vitaly and Cycle