程序员的自我修养(4)

可执行文件的装载与进程

代码经过预编译,编译,汇编,链接过程后生成可执行文件,但可执行文件只有装载到内存程序才可以运行,这节讲可执行文件在linux下的装载过程,以及与进程的关系等等。
1.程序与进程的关系
一个例子,计算机CPU是人,程序菜谱,进程就是炒菜的过程。程序就是预先编译好的指令和数据集合的一个文件,是一个静态的概念。而进程是程序运行时的一个过程。
2.进程虚拟地址空间
程序运行起来后,将会拥有独立的虚拟地址空间,这个地址大小是由CPU位数决定,32位CPU就是0到2的32次方减一。假设32位操作系统我们将有4G的空间,通常以一部分作为操作系统,一部分就留下给进程使用,这些就是可以申请的虚拟地址空间。但是,这些空间由于近代计算机的发展是明显不够的,我们考科一扩展到64位或者使用pae或saw方法。
3.装载的方式
程序执行时所需的指令和数据必须在内存才能够正常运行,最简单的方法是将整个程序直接放入内存,这样程序肯定是能正常运行的,但是肯定会占有大量内存,先现如今,内存还是很昂贵的,明显是不够用。前人就想到了程序运行的时候只要将最常用的数据存放到内存即可,而其它数据放入磁盘,需要的时候在提取至内存。这就提到了两种方法,覆盖装入与页映射。
1.覆盖装入
现在几乎已经淘汰,但是还是值得了解。这种方法主要依靠程序员的在编写的时候将程序分割成若干块,然后使用辅助代码来管理这些模块该何时进入内存,何时被替换。覆盖管理器本身内存很小。
假设有模块A与模块B,之间没有调用关系,互不影响,有一个函数能调用模块A或模块B,那么A与B就共用一片空间,函数调用模块A的时候,模块A之间覆盖这片A与B共同使用的空间,反之一样。当程序一旦很多,这就很需要程序员对各个模块依赖关系组成对应的树状图,从而进行分块和调度策略的分析。
2.页映射
将内存和所有磁盘中的数据与指令按(页)为单位进行划分,以后所有装载和操作的单位就是页,通常一页4K大小,也就是4096字节。我们通常使用先进先出和最少使用算法,当内存使用达到分配的上限,就会最先进入的页会被调出内存,最少使用的页将会调出内存,调入需要的页的数据。
4.进程的建立
进程建立的开始的三件事情,
1.创建一个独立的虚拟地址空间
虚拟空间是由一组页映射函数将虚拟空间的各个页映射至对应的物理空间。那么创建一个虚拟空间并不是创建空间,而是创建映射函数所需要的相应的数据结构,在一些Linux下,创建空间实际上只是分配一个页目录。

2.读取可执行文件头,并且建立虚拟空间与可可执行文件的映射关系
上一步的页映射函数是虚拟空间到物理空间的映射,这一步是虚拟空间与可执行文件的映射关系。我们知道,当程序执行发生页错误时候,物理空间会分配一个物理页,然后将缺少的页面从磁盘读取进入内存,在设置缺少的页面与物理页面的映射关系。那么我们怎么知道这个页在可执行文件那个位置,就需要虚拟空间与可执行文件的映射。

3.将CPU的指令寄存器设置成可执行文件的入口地址,启动运行
就是操作系统执行了一条跳转命令,跳转到了可执行文件的入口,也就是ELF头文件保存的入口地址。

5.操作系统的链接视图与执行视图
执行视图就是将.text,.bss,.data等段分开的视图。链接视图就是将同等属性的段合在一起的视图,例如一代吗段为代表的可读可执行段,以数据段与bss段可读可写的段,以只读数据段为代表的的只读数据段。那么为什么需要这样分配,因为映射到虚拟空间的时候,以页为单位,那么每个段都一个页,会极大的浪费空间,操作系统不管页的内容,只会在乎页的属性,那么我们可以将相同属性的段合在一个页内,就可以极大的节省空间消耗。(这样一个同属性的集合叫做segment)

你可能感兴趣的:(linux,底层)