被隐藏了的过程——编译和链接

        作为一名程序员,从闭着眼也能打出来的“hello world!”到完成一个大型程序,其中离不开对代码的调试与改正。而我今天要给大家说的就是程序运行过程中,那些被系统软件的运行机制与机理所掩盖的一些情况,这样我们在遇到错误的时候就能游刃有余,收放自如了。

        在平常的应用程序开发过程中,我们很少需要关注编译和链接的过程,因为通常都是在集成的开发环境下运行的,这样的开发环境都会将编译和链接的过程一次完成。例如:Visual Studio,Delphi等。因此我们常被这复杂的集成工具所提供的强大功能所迷惑。 今天我就是来解惑的啦~

        重点要开始了~~~开始了~~~开始了~~~ 

        1) 先给大家简单介绍下,虚拟内存和虚拟地址空间,指令和数据。

         指令是指挥计算机工作的命令,在计算机取指阶段从内存中读出的指令流,由控制器处理;

         数据是各种字母,数字,符号和图像等,在计算机执行阶段从内存中读出的数据流,由CPU运算器处理。

         虚拟内存:是操作系统为了解决内存耗尽问题而从硬盘上分出一部分空间充当内存使用。例如,执行某个较大程序时,需要占用较大的内存空间,运行内存耗尽,计算机就会调用硬盘上的硬盘空间给该程序使用,缓解了内存紧张问题。虚拟内存大小与CPU位数有关,CPU位数是指一次能加载运行的整数的宽度,可以定义为ALU算术逻辑单元的宽度或者是数据总线的条数。

         注意:虚拟内存是不存在的,是逻辑上假设出来的。

        虚拟地址空间:是指计算机在创建一个进程时,会为其分配4GB的可访问的地址空间。每个进程都由其自身的虚拟地址空间,范围从0x00000000到0xFFFFFFFF,但不可访问其他进程的虚拟地址空间。每个程序都会与一个4G的虚拟地址空间,0~3G均为用户空间(共享),1G为内核空间(私有)。虚拟地址空间(此图为LINUX下的虚拟空间)如下图所示:

    2)    当程序从编辑完成到执行成功都会经过5个步骤,分别是预处理(Prepressing)、编译(Compilation)、汇编(Assembly)、链接(Linking)和执行(Executing)。程序的运行过程分为两大阶段,编译阶段和链接阶段,而编译阶段又划分为三步预编译、编译和汇编,当编译那一阶段完成时,进行链接。具体细节如下:

    一、预编译(生成.i文件)
        1.宏定义替换(删除#define,并且展开所有的宏定义)
        2.展开头文件(处理#include预编译指令,将包含的文件插入到该预编译指令的位置,采用递归的方式)
        3.删除预编译指令(处理所有的条件预编译指令,例如"#if","#endif","#ifdef","elif","#else"等)
        4.删除注释(删除”//“和"/**/")
        5.添加行号和文件标识
        6.保留#progma

    二、编译(生成.s文件)
        1.词法分析
        2.语法分析
        3.语义分析
        4.代码优化

    三、汇编(生成.o文件,称为可重定位的二进制文件)
        1.将指令代码翻译成二进制

    四、链接(生成.exe文件,称为可执行的二进制文件)
        1.合并段(相同段之间)和符号表
        2.进行符号解析:在符号引用的地方找到符号定义的地方
        3.分配地址和空间
        4.符号的重定位

    五、运行(直接执行链接过程后产生的可执行的二进制文件(.exe文件)即可得到运行结果)

     3)编译阶段

     一、.o文件与目标文件

        编译阶段经过预编译、编译和汇编处理后生成一个.o文件(以Linux系统为例),又编译器编译源代码后生成的文件叫做目标文件。则目标文件就是源代码编译后但未进行连接的那些中间文件(windows下的.obj和Linux下的.o)。目标文件从结构上讲是可执行文件格式,只是还没有经过链接处理,有些符号和地址还没有经过修正,但按照可执行文件存储。在window下可执行文件主要是PE格式,在Linux下可执行文件主要是ELF格式。这里,我们只讨论ELF格式。

     二、.bss段与.data段

        当我们写下如下代码后:


#include<stdio.h>

 
int gdata1 = 10;//.data

int gdata2 = 0;//.bss

int gdata3;//.bss

static int gdata4 = 20;//.data

static int gdata5 = 0;//.bss

static int gdata6;//.bss

 

int main()

{

	int a = 30;//.text

	int b = 0;//.text

	int c;//.text
	

	static int d = 13;//.data

	static int e = 0;//.bss

	static int f;//.bss

	
	return 0;

}

       在vc代码运行中可以看出.data放已初始化且不为0的数据,而.bss放未初始化或为0的数据,其中运行之前以指令形式放在text文件中,运行后,才在栈,堆上开启。在此就不给大家展示具体过程。有机会下次给大家说明。

      .bss段存储的是未定义的全局变量和局部静态变量。但.bss段只是为未初始化的全局变量和局部静态变量预留位置而已,并没有内容,所以在文件中也不占据空间。在这里我们引入强弱符号(c语言中,只关心全局变量)的概念:

  •        强符号:已初始化的全局变量;
  •        弱符号:未初始化的全局变量;

      强弱符号的使用规则:

     1.两强符号:重定义错误
     2.一强一弱符号:选择强符号
     3.两弱符号:选取字节数较大的

 4)执行阶段  

       在编译链接阶段结束后,也就是生成了 可执行的二进制文件;但该文件并不能直接进行运行,因为此时的文件并未在内存中,也就是说,操作系统在运行一个程序时,需要指令和数据,并且必须将所要执行的程序加载到内存上;

       那么,在运行时,需要做以下的事情:

  •     创建虚拟地址和物理内的映射结构体;按照段页式进行映射,以4K大小对齐;
  •     LOAD加载器,将指令和数据加载到内存中;
  •     将第一行指令的地址写入PC寄存器中;

    知识有些不完整,未完待续~~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值