天天看点

【操作系统】程序的编译、装入、链接

下面的内容都是我自己的一些想法,可能不正确,如有错,请指出来,我会及时更改。

首先,我先编写一个main.asm汇编源程序,要明确的是,不同的汇编指令转化成二进制机器指令时,会有多个字节,不同指令所占用的字节是不同的。知道占用多个字节就可以了。

看下图,反汇编下指令的地址和机器指令,我编写的main.asm转化为机器指令,指令的地址是从076B:0000开始的。

【操作系统】程序的编译、装入、链接

编译

编译将我所写的main.asm文件转化为main.obj文件,生成一个目标文件。

转化为目标文件的规则:假设我有一条语句是jmp next反汇编之后,就是变成了jmp 2500,那么这个2500就是相对我自己写的代码段的偏移地址。

再假设我有一条语句是 mov ax,Y 那么转化成机器语言之后,再反汇编就是

mov ax,[0002]就是相对数据段的偏移地址为2的内容送至ax。

虽然程序是按编译->链接->装入的方式执行的,但是可以先跳到装入的内容看一下。

链接

就是将多个目标模块链接成一个大目标模块。

每个模块中的指令地址都是从0开始的。

比如三个目标模块A,B,C

A所有指令地址为0 - L-1, B所有指令地址为0 - M-1,C所有指令地址为0 - N-1。

装入

这些内容都是按着时间线慢慢发展起来的,针对当时的不同的条件,所思考出的不同策略。

1.绝对装入方式

只适合运行单程序。

每次将程序装入内存中初地址为R的地方,那么执行jmp 2500就是跳到jmp R+2500的地方,继续执行。

2.可重定位装入方式

适用于多程序

可重定位就是给cpu配置两个寄存器基址寄存器、变址寄存器

首先,OS会给程序分配一个起始地址,假设10000,保存在基址寄存器中,从这个起始地址,将程序的所有指令依次放入内存中。那么原来的指令jmp 2500放入内存中就会变成jmp 12500。

变址寄存器是存放代码的长度,防止越界的。

3.动态运行时的装入方式

考虑一个程序会被分成多个部分,而这些部分可能会被多次换出,又被多次换入,放入内存中的物理地址就会改变。显然,可重定位装入方式就不适用了。

那么对应其解决的策略就是原本的指令jmp 2500依旧放入内存中,当程序运行时,再将其转化为jmp12500执行,如果,这段代码更改地址了,可能会执行jmp 32500(由这段代码所在物理地址决定)。

再扯一点,因为只有一个基址寄存器,当有多个进程在内存中,那么应该保存哪个进程的起始地址呢?

实际上,每次会给一个刚运行的程序分配一个起始地址,当进程变为阻塞、切换其他进程时,会将这个起始地址保存在这个进程的PCB中。并且会把基址寄存器的内容修改为下一个将要运行进程的起始地址(在PCB中查看是否有起始地址,没有的话则系统分配一个)。

每个进程都有自己的PCB。

继续阅读