上图中,cal指令就是调用了printf()函数,因为这时候printf()函数并不在这个文件中,所以无法确定它的地址,在十六进制中就用“ff ff ff”来表示它的地址。等经过链接以后,这个地址就会变为函数的实际地址,应为连接后这个函数已经被加载进入这个文件中了。
链接的分类:按把A相关的数据或函数合并为一个文件的先后可以把链接分为静态链接和动态链接。
静态链接:
在程序执行之前就完成链接工作。也就是等链接完成后文件才能执行。但是这有一个明显的缺点,比如说库函数。如果文件A和文件B都需要用到某个库函数,链接完成后他们连接后的文件中都有这个库函数。当A和B同时执行时,内存中就存在该库函数的两份拷贝,这无疑浪费了存储空间。当规模扩大的时候,这种浪费尤为明显。静态链接还有不容易升级等缺点。为了解决这些问题,现在的很多程序都用动态链接。
动态链接:和静态链接不一样,动态链接是在程序执行的时候才进行链接。也就是当程序加载执行的时候。还是上面的例子,如果A和B都用到了库函数Fun(),A和B执行的时候内存中就只需要有Fun()的一个拷贝。
关于链接还有很多知识,以后会用专门的文章来谈。这里就不展开讲了。
对装载的简单解释
我们知道,程序要运行是必然要把程序加载到内存中的。在过去的机器里都是把整个程序都加载进入物理内存中,现在一般都采用了虚拟存储机制,即每个进程都有完整的地址空间,给人的感觉好像每个进程都能使用完成的内存。然后由一个内存管理器把虚拟地址映射到实际的物理内存地址。
按照上文的叙述,程序的地址可以分为虚拟地址和实际地址。虚拟地址即她在她的虚拟内存空间中的地址,物理地址就是她被加载的实际地址。
在上文中查看段的时候或许你已经注意到了,由于文件是未链接、未加载的,所以每个段的虚拟地址和物理地址都是0.
加载的过程可以这样理解:先为程序中的各部分分配好虚拟地址,然后再建立虚拟地址到物理地址的映射。其实关键的部分就是虚拟地址到物理地址的映射过程。程序装在完成之后,CPU的程序计数器pc就指向文件中的代码起始位置,然后程序就按顺序执行。
小结一下
写这篇文章的目的在于梳理程序运行的机制,在一个可执行文件执行的背后都隐藏了什么。
从源代码到可执行文件通常要经历许多中间步骤,每一个中间步骤都生成一个中间文件。只是现在的集成开发环境都吧这些步骤影藏了,习惯于集成开发环境的我们也就逐渐的忽略了这些重要的技术内幕。这篇文章也只是介绍了一下这个过程的主线而已。其中的每一个细节展开来讲都可足已用一篇文章来论述。
上面写的多是我个人的理解和看法。有不足的地方、还望能不吝赐教。
本文来源:不详 作者:佚名